一文给你讲清楚BeanFactory 和 FactoryBean 的关联与区别

本文分享自华为云社区 《BeanFactory 和 FactoryBean 的关联与区别》,作者:战斧。

一、概括性的回答

两者其实都是Spring提供的接口,如下

复制代码
public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    boolean isSingleton();
}
复制代码
public interface BeanFactory {
    String FACTORY_BEAN_PREFIX = "&";
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    boolean containsBean(String name);
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    String[] getAliases(String name);
}

BeanFactory 就是我们常说的Spring容器,其内包含着大量的Bean,我们可以从BeanFactory 获取到想要的Bean,或者查询Bean的一些信息。

而FactoryBean则是众多Bean里的一种,只不过这种Bean是一种辅助Bean或者说中间人,它的作用是为你提供另一个/一些Bean。

两者一个比较形象的比喻就是BeanFactory就是一家工厂,我们可以通过提供物品名字,从工厂中得到各式各样的物品,比如桌椅板凳,键盘鼠标 等等。而除此之外,我们还能获取一种比较特殊的物品------生产线(FactoryBean),一般情况下,我们获取生产线当然不是为了它本身,而是为了利用生产线生产出产品,所以当你提供生产线的名字,得到的其实是生产线生产的产品。当然,如果你就是想取这个生产线本身,那你提供的名字就得是 "&" + 生产线名。

二、FactoryBean

FactoryBean示例

我们先来看一下FactoryBean的基础用法,简而言之就是实现FactoryBean接口,然后重写其中的getObject方法,如下:

复制代码
public class ConfigLoaderFactoryBean implements FactoryBean<ConfigLoader> {
    private String configLocation;

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    @Override
    public ConfigLoader getObject() throws Exception {
        if (configLocation.startsWith("file:")) {
            LocalConfigLoader configLoader = new LocalConfigLoader();
            configLoader.setFilePath(configLocation.substring(5));
            return configLoader;
        } else if (configLocation.startsWith("http:")) {
            RemoteConfigLoader configLoader = new RemoteConfigLoader();
            configLoader.setServerUrl(configLocation);
            return configLoader;
        } else {
            throw new IllegalArgumentException("Unsupported config location: " + configLocation);
        }
    }

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

然后把这个factoryBean放入容器中,你可以采用xml或者@Bean等形式注入。

复制代码
<bean id="configLoader" class="com.example.ConfigLoaderFactoryBean">
    <property name="configLocation" value="http://example.com/config.json"/>
</bean

FactoryBean的必要性

我们上面介绍过,FactoryBean其实相当于一个中间人,我们获取它,往往不是需要它本身,而是希望通过它获得另一个Bean,自然的我们会产生疑问,为什么要多此一举?如果我们通过它是为了获得另一个Bean,那么为什么不直接实例化另一个Bean然后放入Spring容器呢?比如在方法上使用@Bean注解。

这种想法无可厚非,主要是因为factoryBean接口的诞生更早,所以早期很多的结构采用了这种方式。后续有了@Bean注解以后,在方法上使用@Bean注解也能实现复杂Bean的创建了。

那是不是所有情况都能使用@Bean来替代factoryBean呢?比如我们想每次获取的Bean都是实时的,又比如我们需要一个计时器Bean,但你注入的Bean都被固定了,只有通过工厂,才能每次获取都能得到一个实时的新Bean。同样的,使用factoryBean还有一个懒加载的作用,对于某些复杂的Bean能在获取时再进行实例化。

三、BeanFactory

BeanFactory与ApplicationContext

提及BeanFactory,自然而然的我们会想到Spring的重要特性IOC,IOC要求有一个能管理所有Bean的管家,而管家需要一个盛放这些Bean的容器,这个容器就是BeanFactory。

尽管我们在日常项目中,使用的容器是具有更全功能的ApplicationContext,但ApplicationContext也是BeanFactory的子接口,其除了单纯的容器功能外,还有配置元信息,应用事件机制,资源管理等功能,所以我们可以说ApplicationContext是BeanFactory的增强版本。

BeanFactory的使用

在早期的spring项目中,我们经常会在代码中指定使用某种BeanFactory ,并且使用如下方式去加载资源。

复制代码
 //读取核心的配置文件
ClassPathResource resource = new ClassPathResource("MyContext.xml");
BeanFactory BeanFactory = new XmlBeanFactory(resource);

顾名思义XmlBeanFactory就是能够读取并解析xml资源,解析出各种Bean后存入自身,而在后期,springboot的大规模使用后,其内置的工厂可以解析xml、properties以及注解等多种配置来源。

当然,其实Spring本身就有相当的自动化程度,比如当我们在启动类上使用。

复制代码
@ImportResource(location = {"classpath:MyContext.xml"})

它也能导入内容,并根据资源后缀是否为".groovy"判断是使用GroovyBeanDefinitionReader.class 还是 XmlBeanDefinitionReader.class,对资源解析完成后,把Bean定义注册进BeanFactory中。

四、总结

我们应当发现了:BeanFactory 和 FactoryBean 除了名字相似、都能包含一些Bean实例之外,其实没有什么相同的地方。前者是SpringIOC的核心,是存放一切Bean的容器;后者只不过是对复杂Bean的一种包装,比如我们常用的myBatis组件,针对各个mapper级接口生成的Bean实例,就是以FactoryBean的形式存在Spring容器中的。

点击关注,第一时间了解华为云新鲜技术~

相关推荐
激流丶14 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue18 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
让学习成为一种生活方式34 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画40 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生1 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
Heavydrink1 小时前
HTTP动词与状态码
java
ktkiko111 小时前
Java中的远程方法调用——RPC详解
java·开发语言·rpc
计算机-秋大田2 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
神里大人2 小时前
idea、pycharm等软件的文件名红色怎么变绿色
java·pycharm·intellij-idea
小冉在学习2 小时前
day53 图论章节刷题Part05(并查集理论基础、寻找存在的路径)
java·算法·图论