Spring之FactoryBean

1.什么是FactoryBean

我们可以将FactoryBean看成Factory + Bean。首先它是一个Bean,和其他Bean一样,归Spring容器管理。同时它也是一个factory可以生产一个定制的Bean。

2.FactoryBean接口

public interface FactoryBean<T> {

    // 返回定制化bean对象
    @Nullable
    T getObject() throws Exception;

    // Spring在查找依赖的时候,通过此方法来判断isTypeMatch
    @Nullable
    Class<?> getObjectType();

    // 默认返回单例对象
    default boolean isSingleton() {
        return true;
    }
}

3.FactoryBean的使用

代码准备

创建POJO ModelA

package com.test.fb.model;

public class ModelA {

    private String sign;

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }
}

创建配置类

package com.test.fb.config;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan("com.test.fb")
public class AppConfig {
}

创建启动类

package com.test.fb;

import com.test.fb.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

    }
}

创建第一个FactoryBean

package com.test.fb.component;

import com.test.fb.model.TargetObject;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component
public class FirstFactoryBean implements FactoryBean<TargetObject> {

    @Override
    public TargetObject getObject() throws Exception {
        TargetObject targetObject = new TargetObject();
        targetObject.setSign("factory create");
        return targetObject;
    }

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

运行main方法,查看运行结果

通过FactoryBean创建了一个定制Bean

4.getBean("beanName") 和 getBean("&beanName") 的区别

  • getBean("beanName") :获取的是定制生产的bean (即FactoryBean#getObject方法返回的对象)
  • getBean("&beanName") :获取是@Component标记的那个类被扫描生成的bean

源码分析

分析AbstractBeanFactory#doGetBean方法

分析transformedBeanName方法(它主要由下方截图的两个方法组成)

  • transformedBeanName :将类似"&&&&beanName"的字符串转成"beanName"
  • canonicalName :bean存在别名的说法,比如beanA的别名是beanB,beanB的别名是beanC,如果调用getBean("beanC")最终会获得beanA

即这个方法目的就是获取原始的beanName

分析getObjectForBeanInstance方法

首先我们要注意一点 : 传入的参数是name,最终解析出来的名称是beanName

我们注意这个if判断,它判断我们原始传入的参数name是不是带有特殊符号"&",如果存在特殊字符表示我们需要的是@Component标记的类产生的bean,而不是FactoryBean#getObject方法产生的bean

如果不带有特殊符号,普通bean直接返回,FactoryBean进入getObjectFromFactoryBean方法

我们跟进去代码继续分析

Spring最终会调用我们自己写的getObject方法

beanFactory有个存放bean的单例池singletonObjects,factoryBean为了保持单例特性将实例出来的bean对象放入factoryBeanObjectCache

5.FactroyBean是懒加载的?

我们利用上文中“FactroyBean的使用”的代码进行测试

我们查看截图,FactoryBean确实是在我们调用genBean方法以后才放入缓存中的,符合懒加载的特性

FactoryBean一定是懒加载的么?我们再写一个例子验证一下

创建第二个FactoryBean

package com.test.fb.component;

import com.test.fb.model.TargetObject;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.stereotype.Component;

@Component
public class SecondFactoryBean implements SmartFactoryBean<TargetObject> {

    @Override
    public TargetObject getObject() throws Exception {
        TargetObject targetObject = new TargetObject();
        targetObject.setSign("second factory create");
        return targetObject;
    }

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

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

执行main方法 查看运行结果

我们可以看到我们还没有调用genBean方法,factoryBeanObjectCache里面已经有缓存了,说明FactoryBean在Spring启动的过程中就实例化了。

我们查看相关源码DefaultListableBeanFactory#preInstantiateSingletons分析原因

总结

  • 非SmartFactoryBean子类实现的FactoryBean是懒加载的
  • SmartFactoryBean的子类实现,并且isEagerInit方法返回值为true的FactoryBean,是非懒加载的

6.关于FactoryBean的思考

FactoryBean的特点是可以定制化生产bean,这一点@Bean同样可以做到,为什么还需要存在FactoryBean呢?而且FactoryBean还有getBean("&beanName")和getBean("beanName")的区别,怎么看都是@Bean比较简单。关于这一点我们可以思考一下@Bean的短板,@Bean只能处理明确目标的bean。比如我希望扫描一个package,让这个package下的类或者接口都变成bean,这样不确定数量和目标类(或接口)的场景,@Bean是无法处理的。我们熟悉的@MapperScan注解在完成mybatis和spring的整合过程中就用到了FactoryBean的特性,所以FactoryBean有不可替代的地位。

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值