• 6. Spring源码篇之FactoryBean


    简介

    在介绍实例化非懒加载的单例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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如何获取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,调用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);
                }
          }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

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

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

    下面就来看下这两个接口

    FactoryBean接口

    public interface FactoryBean<T> {
    
    	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    
    	// 创建出来的Bean对象
    	@Nullable
    	T getObject() throws Exception;
    	
    	@Nullable
    	Class<?> getObjectType();
    	
    	// 默认是创建单例bean
    	default boolean isSingleton() {
    		return true;
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    SmartFactoryBean接口

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

    public interface SmartFactoryBean<T> extends FactoryBean<T> {
       
        // 是否是多例
        default boolean isPrototype() {
            return false;
        }
    
        // 提前实例化
        default boolean isEagerInit() {
            return false;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    使用

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

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

    修改为 SmartFactoryBean,并且isEagerInit返回true

    @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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    控制台输出

    实例化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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    为什么加多个&也可以

    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;
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    通过上面例子我们知道,对于一个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) {
    
        // 判断是不是应该获得一个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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    上面源码的最重要的部分就是,我希望获得的是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) {
                            // 执行后置处理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();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    以上就是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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    欢迎关注,学习不迷路!

  • 相关阅读:
    MVC架构模式实现银行转账
    05-jQuery的ajax
    PBR的工作流
    Mockito和Spock实战
    实现Ubuntu交叉编译程序和Nvida Nano运行程序
    XTTS系列之二:不可忽略的BCT
    esp32 idf 添加的compones找不到头文件
    计算题概念算法
    有关Java发送邮件信息(支持附件、html文件模板发送)
    《下一代互联网(IPv6)搭建与运维》1+X证书
  • 原文地址:https://blog.csdn.net/weixin_44412085/article/details/134478552