Spring BeanFactory和FactoryBean有哪些区别
作者:每天都要进步一点点 发布时间:2023-06-25 07:42:09
一、简介
在Spring中,有这么2个接口:BeanFactory和FactoryBean,名字很相似,很多小伙伴经常混淆,在面试的时候也经常会被问BeanFactory和FactoryBean两者的区别。本篇文章将详细介绍它们的区别,并结合示例,帮助大家对BeanFactory和FactoryBean有一个很好的认识。
二、BeanFactory
BeanFactory是Spring IoC 容器的顶层接口,主要负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
BeanFactory只是一个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,都是附加了某种功能的实现。BeanFactory主要的实现类(包括抽象类):
AbstractBeanFactory:抽象Bean工厂,绝大部分的实现类,都是继承于它;
DefaultListableBeanFactory:Spring默认的工厂类;
XmlBeanFactory:前期使用XML配置用的比较多的时候用的Bean工厂;
AbstractXmlApplicationContext:抽象应用容器上下文对象;
ClassPathXmlApplicationContext:从 xml 的配置文件中获取 bean 并且管理它们;
BeanFactory是Bean工厂,它是一个接口,定义如下:
public interface BeanFactory {
/**
* 区分FactoryBean实例,例如,如果bean命名为myJndiObject是一个FactoryBean,通过&myJndiObject将返回工厂,而不是由工厂返回的实例
*/
String FACTORY_BEAN_PREFIX = "&";
/**
* 返回指定bean的实例
*/
Object getBean(String name) throws BeansException;
/**
* 返回指定bean的实例,并指定返回类型
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* 返回指定bean的实例,并指定创建bean实例时使用的参数
*/
Object getBean(String name, Object... args) throws BeansException;
/**
* 返回的bean实例唯一匹配给定的对象类型
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* 返回的bean实例唯一匹配给定的对象类型,并指定创建bean实例时使用的参数
*/
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
/**
* 返回一个指定bean提供者
*/
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
/**
* 返回一个指定bean提供者
*/
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
/**
* 判断工厂中是否包含给定名称的bean定义,若有则返回true
*/
boolean containsBean(String name);
/**
* 判断bean的作用域是否是singleton:单例模式
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* 判断bena的作用域是否是prototype:多例模式
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* 检查具有指定名称的bean是否与指定的类型匹配
*/
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 检查具有指定名称的bean是否与指定的类型匹配
*/
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
/**
* 用给定的名称确定bean的类型
*/
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
/**
* 用给定的名称确定bean的类型
*/
@Nullable
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
/**
* 返回给定bean名称的所有别名
*/
String[] getAliases(String name);
}
下面列举一下ClassPathXmlApplicationContext的使用示例:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
User user = (User) applicationContext.getBean("user");
user.hello();
三、FactoryBean
一般情况下,Spring通过反射利用bean的class属性指定实现类来实例化bean。在某些情况下,实例化bean的过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。
Spring为此提供了一个FactoryBean工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。
FactoryBean是一个能生产或修饰对象生成的工厂Bean,当我们实现了FactoryBean接口,重写getObject()方法并返回一个实例时,Spring会按照我们指定的内容去注册Bean,来达到定制实例化Bean的效果。
FactoryBean定义如下:
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* 返回需要创建的Bean
*/
@Nullable
T getObject() throws Exception;
/**
* 返回FactoryBean创建的Bean类型
*/
@Nullable
Class<?> getObjectType();
/**
* 是否单例Bean,默认是单例的,存入Spring容器中单例缓存池
*/
default boolean isSingleton() {
return true;
}
}
当配置文件中<bean>的class属性配置的实现类是FactoryBean时,通过getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
下面我们通过一个简单的FactoryBean案例,实现自定义注入Bean对象:
public class Student implements Serializable {
private String id;
private String name;
private int age;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Component
public class MyFactoryBean implements FactoryBean<Student> {
@Override
public Student getObject() throws Exception {
Student student = new Student();
student.setId(UUID.randomUUID().toString());
student.setName("张三");
student.setAge(20);
return student;
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
简单测试一下Spring是否帮助我们创建Student这个Bean对象:
public void test2() {
System.out.println("========返回工厂中的实例========");
//返回工厂中的实例,调用FactoryBean.getObject()创建实例
Student student1 = (Student) applicationContext.getBean("myFactoryBean");
System.out.println(student1);
System.out.println("========返回工厂本身========");
//返回工厂本身,通过构造方法初始化实例
Object bean = applicationContext.getBean("&myFactoryBean");
System.out.println(bean);
}
运行结果如下:
========返回工厂中的实例========
Student{id='5aa54f31-3d4a-4bc0-989a-5149f393c3db', name='张三', age=20}
========返回工厂本身========
com.wsh.springtransactiondemo.factorybean.MyFactoryBean@24e95e44
可以看到,根据"myFactoryBean"的名称获取到的实际上是FactoryBean工厂调用getObject()返回的对象,而不是MyFactoryBean工厂本身,如果要获取MyFactoryBean工厂本身实例,那么需要在名称前面加上'&'符号。如下:
getBean("myFactoryBean"):返回MyFactoryBean工厂中getObject()方法返回的实例对象;
getBean("&myFactoryBean"):返回MyFactoryBean工厂本身的实例;
为什么要使用FactoryBean?
在某些情况下,对于实例Bean对象比较复杂的情况下,如果使用传统方式创建bean会比较复杂,如xml配置繁琐等,于是Spring就提供了FactoryBean接口,让用户通过实现该接口来自定义该Bean接口的实例化过程。
四、总结
Spring 中为我们提供了两种类型的 bean,一种就是普通的 bean,我们通过getBean(id) 方法获得是该 bean 的实际类型,另外还有一种 bean是FactoryBean,也就是工厂 bean,我们通过getBean(id) 获得是该工厂所产生的 Bean的实例,而不是FactoryBean的实例。
BeanFactory:Bean工厂,它是一个用于管理Bean的一个工厂,在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的;
FactoryBean:工厂Bean,它是一个支持我们自定义Bean对象的工厂;
来源:https://weishihuai.blog.csdn.net/article/details/121430801


猜你喜欢
- 将数组元素反转有多种实现方式,这里介绍常见的三种.直接数组元素对换@Testpublic void testReverseSelf() th
- 前言:JSON 是轻量级的数据交换格式,很常用,尤其是在使用 Ajax 时,在后台将数据封装为 JSON 字符串更是常见。之前在做项目的时候
- 1、前言  原先的页面访问地址为:http://127.0.0.1:8888/office/schdul
- 1、题目描述找出数组中重复的数字。在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知
- c语言关闭socket的两种方式一、shutdown()#include<sys/socket.h>int shutdown(i
- 其中包含两个jsp文件,分别为login.jsp和index.jsp代码如下:login.jsp<%@ page language=&
- 1. 前言最近要实现一个小需求,涵盖了很多知识点,比如手势、动画、布局等。挺有意思的,写出来和大家分享一下。如下所示,分为上下两层;当左右滑
- 本文实例为大家分享了Android仿京东分类效果展示的具体代码,供大家参考,具体内容如下1.写一个fragmentimport androi
- Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。它与 JSP,Velocity,FreeMaker 等模
- 1. 添加maven依赖包<dependency> <groupId>org.apache.calcit
- 本文实例讲述了C#记录消息到日志文件的方法。分享给大家供大家参考。具体实现方法如下:public void LogMessageToFile
- 实现效果:注意:using system.io; 往Form1上添加控件picturebox,再添加imagelist,并设置imageli
- 本文实例讲述了C#实现读写ini文件类。分享给大家供大家参考。具体如下:这个C#类封装了对INI配置文件进行操作所需的各种函数,包括读取键值
- 本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下XML布局主页面main布局<com.handma
- 1.EazyEmail邮件发送类库Net 类库自带了邮件发送功能。笔者对该类库,从使用的角度进行了二次封装,nuget上可搜索EazyEma
- java 数据类型:在Java中,数据类型分为两大种:基本数据类型(值类型)和包装类型(引用数据类型)。基本数据类型不是对象,不能调用toS
- String replace replaceFirst repaceAll区别replace(char oldChar, char newC
- Android Lint 原理及使用详解Android Lint 是 ADT 16中引入的新工具,用于扫描 Android 项目源中的潜在错
- 本文为大家分享了android倒计时控件,供大家参考,具体代码如下/* * Copyright (C) 2012 The * Project
- 一、自己封装URLConnection 连接请求类 public void downloadFile1() { try{ &nb