实例区别BeanFactory和FactoryBean

2024-01-10 10:08

本文主要是介绍实例区别BeanFactory和FactoryBean,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

从代码角度写了一个帖子去解释BeanFactory和FactoryBean,感概下Mybatis对Spring框架用的炉火纯青的水平。更多Spring内容进入【Spring解读系列目录】。

BeanFactory

什么是BeanFactory?看名字也能明白这是一个工厂,其实是Spring框架中的一个顶层类。是Spring提供的一个工厂,能够产生注册在Spring中的对象。比如BeanFactory.getBean(),就可以拿到一个bean的实例对象。或者实例化AnnotationConfigApplicationContext类就可以拿到其对象,也就是说BeanFactory是为了产生对象使用的。这个其实没什么好讲的,重点就在于FactoryBean

BeanFactory bean=new AnnotationConfigApplicationContext();
bean.getBean("service");

FactoryBean

FactoryBean是Spring中的一个特殊的bean。什么是bean呢?其实只要一个类交给Spring容器管理就可以称作一个Bean,也可以直接说就是一个对象。那FactoryBean有什么特殊性呢?那就是FactoryBean可以构造一个新的bean出来。我们先用一个例子来简单说下,先创建一个测试类TestDaoFactoryBean,然后创建一个MyDaoFactroyBean类实现FactoryBean,再实现里面的方法,起个名字叫factoryBeanTest

public class TestDaoFactoryBean { }
@Component("factoryBeanTest")
public class MyFactroyBean implements FactoryBean {@Overridepublic Object getObject() throws Exception {return new TestDaoFactoryBean(); //getObject返回新构建的类}@Overridepublic Class<?> getObjectType() {return TestDaoFactoryBean.class;}@Overridepublic boolean isSingleton() {  //设置单例模式return true;}
}

构建完成以后我们就尝试去把MyFactroyBean拿出来,所以我们用一个Test类取一下:

public class Test {public static void main(String[] args) {AnnotationConfigApplicationContext anno=new AnnotationConfigApplicationContext(AppConfig.class);System.out.println(anno.getBean("factoryBeanTest"));}
}
打印结果:
com.demo.dao.TestDaoFactoryBean@701fc37a

最终运行的结果发现取出来的并不是MyFactroyBean,而是我们在getObject()这个方法里返回的对象。那么怎么拿到MyFactroyBean呢,只要把名字前面加一个”&”符号就可以了

System.out.println(anno.getBean("&factoryBeanTest"));
打印结果:
com.demo.dao.MyFactroyBean@701fc37a

为什么会这样呢?先不解释,不过根据这个现象我们总结一下:
如果一个类实现了FactoryBean,那么Spring容器中就会生成两个对象,一个是getObject()方法返回的对象,还有一个是当前的对象(也就是this本身)。getObject()对象存的是指定的对象,在Bean上指定名字的对象。当前对象是存的是”&”+当前类的名字

FactoryBean的作用

要想解释为什么Spring要这样设计,首先我们看先看FactoryBean有什么具体的作用。做一个假设,我们需要引用第三方的一个jar包,这里面有一个配置类A,而这个配置类A又依赖了其他100个类。那么请问,这个其他的100个类的依赖,应该由谁去管理呢?不管由谁管理,一定不是让使用者管理对不对,我们期望的是在Spring中配置了一个A的bean,就可以正常使用这个jar包了,而不是说要使用者把A中所有的依赖都维护一遍,这显然不合理。比如我们经常使用的Mybatis就是一个非常经典的例子。

一般来说都会把Mybatis交给Spring管理,如果Mybatis里面使用的第三方的类也都需要开发人员去维护是不是极为不合理。所以我们期望就是Mybatis自己维护好自己的关系,然后提供一个bean(对象)出来,我只要维护这个bean就可以完成Spring的管理了。以Mybatis中的SqlSessionFactory为例。如果要我们自己配Spring依赖关系,首先要配一个SqlSessionFactorybean,然后发现里面依赖了Configuration对象,又要配置一堆依赖关系,配完以后Configuration对象又依赖了Environment对象和好几十个其他对象,要一个一个配bean出来,单单交给Spring管理那就是一个相当巨大的工程了,还不如自己写来的简单。所以最好的办法就是Mybatis自己把这一切的关系都维护好了,使用者只要引用SqlSessionFactory一个bean就搞定了,这个时候FactoryBean就有大作用了。Mybatis也提供了这样一个bean叫做SqlSessionFactoryBean就做了这样一个事情。

#Mybatis提供的FactorBean:
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {...private SqlSessionFactory sqlSessionFactory;...@Overridepublic SqlSessionFactory getObject() throws Exception {if (this.sqlSessionFactory == null) {afterPropertiesSet();}return this.sqlSessionFactory;}...
}

这个类也实现了FactoryBean,虽然叫做SqlSessionFactoryBean但是它最终返回的是SqlSessionFactory。在SqlSessionFactoryBean中Mybatis就把ConfigurationEnvironment等等这些对象提前初始化,并且用setter方法赋值,那么最终我们拿到的bean就是已经初始化好了对象直接可以用。

FactoryBean使用范例

所以为了加深理解,我们也可以做一个例子,首先我们改造一下TestDaoFactoryBean让其具备一些属性。

public class TestDaoFactoryBean {private String a1;private Integer a2;public String getA1() {return a1;}public void setA1(String a1) {this.a1 = a1;}public Integer getA2() {return a2;}public void setA2(Integer a2) {this.a2 = a2;}
}

然后修改MyFactroyBean,并且在里面初始化TestDaoFactoryBean

@Component("factoryBeanTest")
public class MyFactroyBean implements FactoryBean {@Overridepublic Object getObject() throws Exception {TestDaoFactoryBean factoryBean=new TestDaoFactoryBean();factoryBean.setA1("a1");factoryBean.setA2(22222);return factoryBean;}@Overridepublic Class<?> getObjectType() {return TestDaoFactoryBean.class;}@Overridepublic boolean isSingleton() {return true;}
}

最后获取一下,这样我们就完成了TestDaoFactoryBean的初始化赋值,外部如果要使用就会发现其中的属性a1a2已经被构建过了。

public class Test {public static void main(String[] args) {AnnotationConfigApplicationContext anno=new AnnotationConfigApplicationContext(AppConfig.class);TestDaoFactoryBean factoryBean= (TestDaoFactoryBean) anno.getBean("factoryBeanTest");System.out.println("getA1:"+factoryBean.getA1());System.out.println("getA2:"+factoryBean.getA2());}
}
打印结果:
getA1:a1
getA2:22222

但是要注意,实现了FactoryBean的本身是和别的bean没有区别的,唯一的区别就是返回的对象要指定,因此这些实现了FactoryBeanbean也可以进行业务实现。当然这点要根据具体的业务具体去分析。

总结

举个不恰当的例子BeanFactory是个鞋厂,FactoryBean就是一个特殊的鞋子,这个鞋子能够根据需求产生新的鞋子,这么牛逼的鞋子,就问你服不服。从名字上看BeanFactorybean工厂只能生产bean。而FactoryBean是一个bean (对象),但是这个bean能够生产新的bean。好绕,大家理解下。

那么如果有人问道这两个的区别,大概可以这样说:BeanFactory是Spring容器当中的工厂,它能生产我们交给Spring的类,并且获取这些类的对象。FactoryBean只是Spring中的一个特殊的bean接口,FactoryBean需要被实现三个方法,实现以后可以返回一个新的bean,这个新的bean就是getObject()返回出去的对象。并且存在的形式也不同,得到FactoryBean本身需要用&+类名,而要得到FactoryBean产生的对象则要使用真实指定的bean名字。

这篇关于实例区别BeanFactory和FactoryBean的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/590430

相关文章

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

JSONArray在Java中的应用操作实例

《JSONArray在Java中的应用操作实例》JSONArray是org.json库用于处理JSON数组的类,可将Java对象(Map/List)转换为JSON格式,提供增删改查等操作,适用于前后端... 目录1. jsONArray定义与功能1.1 JSONArray概念阐释1.1.1 什么是JSONA

MyBatis中$与#的区别解析

《MyBatis中$与#的区别解析》文章浏览阅读314次,点赞4次,收藏6次。MyBatis使用#{}作为参数占位符时,会创建预处理语句(PreparedStatement),并将参数值作为预处理语句... 目录一、介绍二、sql注入风险实例一、介绍#(井号):MyBATis使用#{}作为参数占位符时,会

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化