6. Spring源码篇之FactoryBean

简介

在介绍实例化非懒加载的单例Bean之前,先了解一下FactoryBean

这是spring提供的一个非常重要的功能,是一个小型的工厂,可以灵活的创建出需要的Bean,在很多框架与spring整合的过程中都会用到,例如Mybatis-plus,需求是写一个Mapper注解就能成为Spring的Bean,那么这种批量动态生产Bean的功能就需要用到FactoryBean

源码解析

spring如何判断是一个FactoryBean

java 复制代码
// 首先把Bean的class弄出来,并不实例化Bean
beanType = isFactoryBean -> predictBeanType-> resolveBeanClass-> doResolveBeanClass -> Class.forName

// 有了class就可以判断了
FactoryBean.class.isAssignableFrom(beanType)

其实就是使用的 Class.forName 生成了class

如何获取FactoryBean

以下是实例化非懒加载单例Bean的部分源码

java 复制代码
// 通过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,调用getObject
        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) {
                // SmartFactoryBean && isEagerInit返回true
                getBean(beanName);
            }
      }
}

从上面代码可以看出,如果是一个FactoryBean,在实例化非懒加载的单例Bean的时候FactoryBean就会实例出来,如果实现的是SmartFactoryBean,那么真正的Bean也会在这个步骤实例化出来

还可以看出要获取FactoryBean,需要在beanName前面加一个&

下面就来看下这两个接口

FactoryBean接口

java 复制代码
public interface FactoryBean<T> {

	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

	// 创建出来的Bean对象
	@Nullable
	T getObject() throws Exception;
	
	@Nullable
	Class<?> getObjectType();
	
	// 默认是创建单例bean
	default boolean isSingleton() {
		return true;
	}

}

SmartFactoryBean接口

SmartFactoryBean继承了FactoryBean,可以决定是否提前实例化对象

java 复制代码
public interface SmartFactoryBean<T> extends FactoryBean<T> {
   
    // 是否是多例
    default boolean isPrototype() {
        return false;
    }

    // 提前实例化
    default boolean isEagerInit() {
        return false;
    }

}

使用

java 复制代码
public class UserBean {

    public UserBean() {
        System.out.println("实例化UserBean");
    }

}


@Component
public class UserFactoryBean implements FactoryBean<UserBean> {
    @Override
    public UserBean getObject() throws Exception {
        return new UserBean();
    }

    @Override
    public Class<?> getObjectType() {
        return UserBean.class;
    }
}




public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

上面代码运行的时候控制台什么都不会打出,也就是并没有实例化UserBean

修改为 SmartFactoryBean,并且isEagerInit返回true

java 复制代码
@Component
public class UserFactoryBean implements SmartFactoryBean<UserBean> {
    @Override
    public UserBean getObject() throws Exception {
        return new UserBean();
    }

    @Override
    public Class<?> getObjectType() {
        return UserBean.class;
    }

    @Override
    public boolean isEagerInit() {
        return true;
    }
}

控制台输出

实例化UserBean

获取Bean

既然FactoryBean可以通过getObject生成一个新的Bean,那么这个新的Bean如何获得,FactoryBean又如何获取,前面也介绍了,可以加一个&,下面来获取一下

java 复制代码
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通过一个循环

java 复制代码
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,

java 复制代码
// 一个缓存,缓存了通过FactoryBean的getObject获得的对象,key为不带&的
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);

protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    // 判断是不是应该获得一个FactoryBean
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        // 进入这表示beanInstance应该要是一个FactoryBean
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // 不是的话就报错
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
        if (mbd != null) {
            mbd.isFactoryBean = true;
        }
        return beanInstance;
    }

    
    // 能执行到这里,那么期望beanInstance不是一个FactoryBean
    if (!(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源码分析

java 复制代码
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) {
                        // 执行后置处理postProcess
                        object = postProcessObjectFromFactoryBean(object, beanName);
                    }
                    if (containsSingleton(beanName)) {
                        // 缓存起来
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    else {
        // 表示不是单例,那么直接调用getObject返回就行了
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
             // 执行后置处理postProcess
             object = 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将在后面介绍,源码先展示

java 复制代码
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;
    }
}

欢迎关注,学习不迷路!

相关推荐
java小吕布10 分钟前
Java集合框架之Collection集合遍历
java
一二小选手12 分钟前
【Java Web】分页查询
java·开发语言
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ24 分钟前
idea 弹窗 delete remote branch origin/develop-deploy
java·elasticsearch·intellij-idea
Code成立27 分钟前
《Java核心技术 卷I》用户图形界面鼠标事件
java·开发语言·计算机外设
鸽鸽程序猿1 小时前
【算法】【优选算法】二分查找算法(下)
java·算法·二分查找算法
瓜牛_gn1 小时前
Spring Security概述
spring
遇见你真好。1 小时前
自定义注解进行数据脱敏
java·springboot
NMBG221 小时前
[JAVAEE] 面试题(四) - 多线程下使用ArrayList涉及到的线程安全问题及解决
java·开发语言·面试·java-ee·intellij-idea
王二端茶倒水1 小时前
大龄程序员兼职跑外卖第五周之亲身感悟
前端·后端·程序员
像污秽一样1 小时前
Spring MVC初探
java·spring·mvc