目录
前言:
目前也是金三银四跳槽的好时机,很多小伙伴在面试的时候被问到Spring框架时,可能会被问到BeanFactory和FactoryBean的区别,所以特意写一篇帖子来仔细讲解到2者的区别。
正文:
BeanFactory是什么?
BeanFactory可以理解是一个生成bean对象工厂的一个最底层的一个接口,任何的bean工厂实现的一个base接口。从中定义了为创建bean的一系列方法,并且在接口注释上规定了一个bean的生命周期。
FactoryBean是什么?
FactoryBean就是一个bean对象,会通过BeanFactory生产进IOC容器中。而FactoryBean接口也是Spring的一个高扩展点,用户可以通过FactoryBean接口自己生产Bean,从一定角度来说可以避免掉IOC上下文加载和Bean工厂创建bean的一个复杂流程。
FactoryBean的使用
// FactoryBean接口的实现类,注意一定要加上@Component注入到IOC容器中
@Component
public class MyFactoryBean implements FactoryBean<User> {
// 手动创建对象,如果isSingleton()方法设置了为单例的话,此方法只会在底层运行一次
@Override
public User getObject() throws Exception {
return new User();
}
// 对象的类型
@Override
public Class<?> getObjectType() {
return User.class;
}
// 是否单例,这影响到getObject在底层的调用
@Override
public boolean isSingleton() {
return true;
}
}
// 配置类扫描注解
@Configuration
@ComponentScan("test")
public class MyConfig {
}
// Bean对象
public class User {
public User(){
System.out.println("无参被调用");
}
}
// 主启动类
public class Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Object bean1 = applicationContext.getBean("myFactoryBean");
Object bean2 = applicationContext.getBean("myFactoryBean");
System.out.println(bean1);
System.out.println(bean2);
System.out.println(bean1==bean2);
}
}
通过实现FactoryBean接口重写他的三个方法来手动new对象和控制对象,三个方法的注释我在代码中有做一个讲解。
这里我们可能会存在一个疑问,我代码写的是applicationContext.getBean("myFactoryBean"),为什么返回的是User对象呢?底层是如何控制的单例呢?
这里疑问后面源码中会有讲,并且再思考,可能有些小伙伴之前有过了解的知道BeanFactory工厂来识别正常bean对象和FactoryBean对象是FactoryBean对象的name最前面会加上&(但是注入到IOC容器中并不会,后面源码会讲解),那我们在容器加载完后,通过getBean()方法获取到的是User对象,那我们要获取到FactoryBean对象是不是通过&+beanName呢?那获取到以后通过FactoryBean的getObject()能不能获取到User对象呢?User对象是否还单例呢?
对以上问题我们想通过代码来证实,在通过源码来证实。
// 主启动类代码
public class Application {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
MyFactoryBean fb1 = (MyFactoryBean)applicationContext.getBean("&myFactoryBean");
User user1 = fb1.getObject();
User user2 = fb1.getObject();
System.out.println(user1==user2);
}
}
改变主启动类通过运行得知,获取到FactoryBean是通过&+beanName,但是User并不是单例的,所以我们源码阶段来解答以上所有的问题。
FactoryBean的底层源码解读
明白IOC容器启动原理的小伙伴们知道就是围绕着AbstractApplicationContext类的refresh()方法来启动整个上下文。而我们的FactoryBean前面也说了就是一个Bean对象将通过BeanFactory工厂对象来创建添加到IOC容器中,所以我们的入口就是在finishBeanFactoryInitialization()方法中。
接着就进入到preInstantiateSingletons()方法中
这里获取到refresh()方法中前几个方法解析到的BeanDefinition对象,然后就产生了分流,一个是FactoryBean对象,一个是普通Bean对象,我们是FactoryBean,我们看看是如何判断是否是FactoryBean对象。
很显然MyFactoryBean是FactoryBean的子类,所以返回为true,并且将isFactoryBean设置为true
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
getBean()方法无非就是就是查缓存,缓存中没有就创建,并且创建的名称是&+beanName。
对了博主前面不是说添加到IoC容器中不是没有&吗?那么我们往getBean()方法里面追一把。
可以证明他是去掉了&标识符的,后面就是查一级缓存中是否已经存在,存在返回,不存在就创建,这里就不追了并不是我们的重点,所以我们就回到主启动类中的代码。
前面的代码是IoC容器上下文的创建和Bean的创建,执行完毕后,所以现在运行main方法中其他代码,所以我们追到断点行的getBean()方法中。
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
// Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// Return bean instance from factory.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// Caches object obtained from FactoryBean if it is a singleton.
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
核心代码就在这里了,我们在演示FactoryBean的代码时候在启动类写过2种写法。
一种是&+beanName,一种就直接是beanName,两种的返回值不一样的核心代码也就在这里。
看到if (BeanFactoryUtils.isFactoryDereference(name))
这里是判断name是否是&标志位开头,显然我们现在的测试代码就只有beanName,所以是进不到if代码块,而另一个测试代码带上了&标志位所以是进入到if代码块中。
目前我们是没有标志位的,所以走的代码如下
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
object = doGetObjectFromFactoryBean(factory, beanName);
// Only post-process and store if not put there already during getObject() call above
// (e.g. because of circular reference processing triggered by custom getBean calls)
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
beforeSingletonCreation(beanName);
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}
执行代码都在这里了,FactoryBean对象也获取到了,岂不是可能为所欲为的执行他里面的三个方法了,首先先判断是否是单例bean,再其次就是查缓存了,缓存中有就直接返回没有就执行getObject()方法并且返回对象并且添加进缓存中吗,如果实现了BeanPostProcessor接口前后置操作将会走前后置操作,并且会覆盖getObject()方法返回的bean对象。看到非单例对象else代码块中,并不会走缓存,所以每次都是生成一个新的bean对象,并且如果实现了BeanPostProcessor接口也会走前后置操作,并且生成的bean对象也会覆盖getObject()对象。并且最后的bean对象将作为返回值返回。
不带标志位的追完了,那我们追一下带标志位的。
// Don't let calling code try to dereference the factory if the bean isn't a factory.
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
}
这里其实没啥好讲的其实就是判断是否携带&标志位,带了就返回FactoryBean对象。然后返回到主启动类中,FactoryBean对象调用getObject()对象。
而每次调用getObject()方法都会手动new一个对象,所以肯定不是单例的,每次都会不一样。
带&标识符和不带的区别
其实追到这里能明白带&和不带区别就是不带&标识符将getObejct()方法的返回值缓存中缓存中,所以就只创建了一次,并且返回值是getObejct()方法new的对象,前提是单例。带&标识符的情况就是直接返回FactoryBean对象。
总结:
算比较容易的源码了。作者写帖不易,希望帮到大家的同时可以对作者一个支持。希望点赞+关注