0
点赞
收藏
分享

微信扫一扫

源码解读Spring中FactoryBean和BeanFactory区别

目录

前言:

正文:

BeanFactory是什么?

FactoryBean是什么?

FactoryBean的使用

FactoryBean的底层源码解读

带&标识符和不带的区别

总结:


前言:

目前也是金三银四跳槽的好时机,很多小伙伴在面试的时候被问到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对象。

总结:

算比较容易的源码了。作者写帖不易,希望帮到大家的同时可以对作者一个支持。希望点赞+关注

举报

相关推荐

0 条评论