6. Spring源码篇之FactoryBean
最佳答案 问答题库668位专家为你答疑解惑
简介
在介绍实例化非懒加载的单例Bean之前,先了解一下FactoryBean
这是spring提供的一个非常重要的功能,是一个小型的工厂,可以灵活的创建出需要的Bean,在很多框架与spring整合的过程中都会用到,例如Mybatis-plus,需求是写一个Mapper注解就能成为Spring的Bean,那么这种批量动态生产Bean的功能就需要用到FactoryBean
源码解析
spring如何判断是一个FactoryBean
// 首先把Bean的class弄出来,并不实例化Bean
beanType = isFactoryBean -> predictBeanType-> resolveBeanClass-> doResolveBeanClass -> Class.forName// 有了class就可以判断了
FactoryBean.class.isAssignableFrom(beanType)其实就是使用的 Class.forName 生成了class
如何获取FactoryBean
以下是实例化非懒加载单例Bean的部分源码
// 通过class去判断,如果现在要实例化的Bean是一个FactoryBean,进入判断
if (isFactoryBean(beanName)) {// 带&表示要实例化FactoryBean,不管前面有多少个&,都会只变成一个Object bean = getBean("&" + beanName);if (bean instanceof FactoryBean) {FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;// 如果是SmartFactoryBean 会有一个isEagerInit,如果isEagerInit返回true,那么会马上实例化真正的Bean,调用getObjectif (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) {// SmartFactoryBean && isEagerInit返回truegetBean(beanName);}}
}
从上面代码可以看出,如果是一个FactoryBean,在实例化非懒加载的单例Bean的时候FactoryBean就会实例出来,如果实现的是SmartFactoryBean,那么真正的Bean也会在这个步骤实例化出来
还可以看出要获取FactoryBean,需要在beanName前面加一个&
下面就来看下这两个接口
FactoryBean接口
public interface FactoryBean<T> {String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";// 创建出来的Bean对象@NullableT getObject() throws Exception;@NullableClass<?> getObjectType();// 默认是创建单例beandefault boolean isSingleton() {return true;}}
SmartFactoryBean接口
SmartFactoryBean继承了FactoryBean,可以决定是否提前实例化对象
public interface SmartFactoryBean<T> extends FactoryBean<T> {// 是否是多例default boolean isPrototype() {return false;}// 提前实例化default boolean isEagerInit() {return false;}}
使用
public class UserBean {public UserBean() {System.out.println("实例化UserBean");}}@Component
public class UserFactoryBean implements FactoryBean<UserBean> {@Overridepublic UserBean getObject() throws Exception {return new UserBean();}@Overridepublic Class<?> getObjectType() {return UserBean.class;}
}public class Application {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);}
}
上面代码运行的时候控制台什么都不会打出,也就是并没有实例化UserBean
修改为 SmartFactoryBean,并且isEagerInit返回true
@Component
public class UserFactoryBean implements SmartFactoryBean<UserBean> {@Overridepublic UserBean getObject() throws Exception {return new UserBean();}@Overridepublic Class<?> getObjectType() {return UserBean.class;}@Overridepublic boolean isEagerInit() {return true;}
}
控制台输出
实例化UserBean
获取Bean
既然FactoryBean可以通过getObject生成一个新的Bean,那么这个新的Bean如何获得,FactoryBean又如何获取,前面也介绍了,可以加一个&,下面来获取一下
public class Application {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);System.out.println(context.getBean("userFactoryBean"));System.out.println(context.getBean("&userFactoryBean"));System.out.println(context.getBean("&&&userFactoryBean"));}
}实例化UserBean
com.shura.beans.UserBean@77556fd
com.shura.beans.UserFactoryBean@368239c8
com.shura.beans.UserFactoryBean@368239c8
为什么加多个&也可以
spring通过一个循环
public static String transformedBeanName(String name) {// 没有&直接返回if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {return name;}return transformedBeanNameCache.computeIfAbsent(name, beanName -> {do {// 循环截取掉 &beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());}while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); // 如果还有&继续截取return beanName;});
}
通过上面例子我们知道,对于一个FactoryBean,其实会实例化两个Bean,一个是FactoryBean本身,一个是通过getObject方法获取出来的Bean
通过beanName获取的是getObject方法获取出来的Bean,如果beanName前面加上一个或者多个&符号,那么获取的是FactoryBean
何时调用getObject
在spring中每实例化完Bean后,都会接着这么一行代码
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
sharedInstance为实例化出来的对象,name为传入进来的名字如&userFactoryBean,beanName便是处理后的name为 userFactoryBean,
// 一个缓存,缓存了通过FactoryBean的getObject获得的对象,key为不带&的
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {// 判断是不是应该获得一个FactoryBeanif (BeanFactoryUtils.isFactoryDereference(name)) {// 进入这表示beanInstance应该要是一个FactoryBeanif (beanInstance instanceof NullBean) {return beanInstance;}// 不是的话就报错if (!(beanInstance instanceof FactoryBean)) {throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());}if (mbd != null) {mbd.isFactoryBean = true;}return beanInstance;}// 能执行到这里,那么期望beanInstance不是一个FactoryBeanif (!(beanInstance instanceof FactoryBean)) {// 确实不是,那么就返回,通常情况我们的Bean就在这返回return beanInstance;}// 后面逻辑表示,期望不是FactoryBean,是现在得到的实例是一个FactoryBean,那么应该要调用getObject获得真正对象Object object = null;if (mbd != null) {mbd.isFactoryBean = true;} else {// 从缓存中拿一下object = getCachedObjectForFactoryBean(beanName);}if (object == null) {// 到这肯定确定beanInstance是一个FactoryBean,直接强转FactoryBean<?> factory = (FactoryBean<?>) beanInstance;// Caches object obtained from FactoryBean if it is a singleton.if (mbd == null && containsBeanDefinition(beanName)) {mbd = getMergedLocalBeanDefinition(beanName);}// synthetic为true,表示是由硬编码创建的可以不用进行PostProcessor,其实也表示不允许改动boolean synthetic = (mbd != null && mbd.isSynthetic());object = getObjectFromFactoryBean(factory, beanName, !synthetic);}return object;
}
上面源码的最重要的部分就是,我希望获得的是getObject返回的bean,但是现在得到的是一个FactoryBean,那么就调用getObject返回真正的Bean
getObjectFromFactoryBean源码分析
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {if (factory.isSingleton() && containsSingleton(beanName)) {synchronized (getSingletonMutex()) {// 如果是单例会在 factoryBeanObjectCache 缓存,那么就要先从缓存中取,这里很多判断是用来保证bean只实例化一次的逻辑,可以不用看Object object = this.factoryBeanObjectCache.get(beanName);if (object == null) {// 执行getObject()方法,返回的object不可能为null(会返回NullBean)object = doGetObjectFromFactoryBean(factory, beanName);// 可能另一个线程已经创建Object alreadyThere = this.factoryBeanObjectCache.get(beanName);if (alreadyThere != null) {object = alreadyThere;}else {if (shouldPostProcess) {// 执行后置处理postProcessobject = postProcessObjectFromFactoryBean(object, beanName);}if (containsSingleton(beanName)) {// 缓存起来this.factoryBeanObjectCache.put(beanName, object);}}}return object;}}else {// 表示不是单例,那么直接调用getObject返回就行了Object object = doGetObjectFromFactoryBean(factory, beanName);if (shouldPostProcess) {// 执行后置处理postProcessobject = postProcessObjectFromFactoryBean(object, beanName);}return object;}
}private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {// 去掉一些异常判断逻辑,就是调用的getObject方法return factory.getObject();
}
以上就是FactoryBean的所有逻辑了,最终是调用的getObject获取Bean
Mybatis-plus应用FactoryBean
例如 MapperFactoryBean,Mybaties将在后面介绍,源码先展示
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {private Class<T> mapperInterface;private boolean addToConfig = true;public MapperFactoryBean() {}public MapperFactoryBean(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}protected void checkDaoConfig() {super.checkDaoConfig();Assert.notNull(this.mapperInterface, "Property 'mapperInterface' is required");Configuration configuration = this.getSqlSession().getConfiguration();if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {try {configuration.addMapper(this.mapperInterface);} catch (Exception var6) {this.logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", var6);throw new IllegalArgumentException(var6);} finally {ErrorContext.instance().reset();}}}public T getObject() throws Exception {return this.getSqlSession().getMapper(this.mapperInterface);}public Class<T> getObjectType() {return this.mapperInterface;}public boolean isSingleton() {return true;}public void setMapperInterface(Class<T> mapperInterface) {this.mapperInterface = mapperInterface;}public Class<T> getMapperInterface() {return this.mapperInterface;}public void setAddToConfig(boolean addToConfig) {this.addToConfig = addToConfig;}public boolean isAddToConfig() {return this.addToConfig;}
}
欢迎关注,学习不迷路!
99%的人还看了
相似问题
- 详解Python安装requests库的实例代码
- 腾讯云4核8G服务器配置价格表,轻量和CVM标准型S5实例
- 类方法,静态方法和实例方法的区别及应用场景
- C#WPF用户控件及自定义控件实例
- 【机器学习】 逻辑回归算法:原理、精确率、召回率、实例应用(癌症病例预测)
- C语言童年生活二三事(ZZULIOJ1091:童年生活二三事(多实例测试))
- QT基础入门【QSS】QT伪状态类型和实例
- spider 网页爬虫中的 AWS 实例数据获取问题及解决方案
- 适合小白的超详细yolov8环境配置+实例运行教程,从零开始教你如何使用yolov8训练自己的数据集(Windows+conda+pycharm)
- python:list和dict的基本操作实例
猜你感兴趣
版权申明
本文"6. Spring源码篇之FactoryBean":http://eshow365.cn/6-38513-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!