IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring之FactoryBean的使用与源码分析 -> 正文阅读

[Java知识库]Spring之FactoryBean的使用与源码分析

FactoryBean介绍

FactoryBean是Spring提供的一个可以由用户自定义的Bean实例工厂接口,FactoryBean的对象本身由Spring容器管理,但FactoryBean仅作为对象工厂暴露,其生产出的Bean的过程由用户控制。

FactoryBean接口提供了如下方法:

方法描述
T getObject()获取该FactoryBean所生产的实际对象, BeanFactory在获取FactoryBean生产的实际对象时,会根据其isSingleton()方法来判定是否创建的对象属于singleton或是prototype
Class<?> getObjectType();返回工厂生产的实际对象类型。
boolean isSingleton();所生产的对象是否是单例;此方法告知beanFactory,每次获取该工厂的bean对象时是每次调用getObject()来获取新的对象,还是始终获取唯一的单例对象。

FactoryBean的使用

使用示例

package com.baiyang.factorybean;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.Serializable;

class Book implements Serializable {
    private static final long serialVersionUID = -672687543464668808L;

    private String name;
    private String author;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}
public class BookFactory implements FactoryBean<Book> {
    public static boolean isSingleton = true;

    @Override
    public Book getObject() throws Exception {
        Book book = new Book();
        book.setName("《非暴力沟通》");
        book.setAuthor("马歇尔·卢森堡");
        return book;
    }

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

    @Override
    public boolean isSingleton() {
        return isSingleton;
    }

    public static void main(String[] args) {
        System.out.println("-------工厂singleton模式------");
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("factorybean.xml");
        Book book1 = (Book) context.getBean("book");
        System.out.println("book1:" + book1);
        Book book2 = (Book) context.getBean("book");
        System.out.println("book2:" + book2);
        System.out.println("book1 == book2:" + (book1 == book2));

        System.out.println("-------工厂prototype模式------");
        BookFactory.isSingleton = false;
        context = new ClassPathXmlApplicationContext("factorybean.xml");
        book1 = (Book) context.getBean("book");
        System.out.println("book1:" + book1);
        book2 = (Book) context.getBean("book");
        System.out.println("book2:" + book2);
        System.out.println("book1 == book2:" + (book1 == book2));
    }
}

配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--定义bean工厂-->
    <bean id="book" class="com.baiyang.factorybean.BookFactory"/>

</beans>

运行结果

-------工厂singleton模式------
book1:com.baiyang.factorybean.Book@5bcab519
book2:com.baiyang.factorybean.Book@5bcab519
book1 == book2:true
-------工厂prototype模式------
book1:com.baiyang.factorybean.Book@73846619
book2:com.baiyang.factorybean.Book@4bec1f0c
book1 == book2:false

可以从结果中看出:

  • 尽管在xml中配置的id为"book"的对象是BookFactory类型,但通过getBean("book")获取的类型是Book类型,非BookFactory类型
  • FactoryBean#isSingleton()返回true时多次获取的book对象都是同一个对象。
  • FactoryBean#isSingleton()返回false时多次获取的book对象都是不同的对象。

获取FactoryBean自身工厂对象

如果需要获取FactoryBean自身的工厂对象需要在BeanId前加上"&“符号;”&“符号可以理解为C语言中获取变量的地址;在此处可以理解为"beanName"如果是工厂bean,则获取的是对应工厂生产的Bean,而”&beanName"获取的是生产该"beanName"的工厂;

System.out.println("--------获取FactoryBean自身对象-------");
System.out.println("BookFactory:" + context.getBean("&book"));

FactoryBean的好处

在这里插入图片描述

FactoryBean与BeanFactory的区别

两者都是用来创建对象的,当使用BeanFactory对象的时候必须遵循完整的创建过程,这个过程是由Spring容器来管理控制的。而FactoryBean只需要调用getObject方法就可以返回对象了,整个对象的创建过程是由用户来控制的。

源码分析

FactoryBean的缓存区

在这里插入图片描述
FactoryBeanRegistrySupport类实现了对FactoryBean的支持;我们熟知的最常用的Bean工厂DefaultListableBeanFactory继承了FactoryBeanRegistrySupport,从而也获得了FactoryBean的支持。

执行流程

在这里插入图片描述

源码解析

FactoryBean的初始化过程

首先看下BeanFactory实例化所有BeanDefinition的过程,来了解下FactoryBean的初始化过程。
先定位到AbstractApplicationCountext类的refresh()方法的finishBeanFactoryInitialization(beanFactory);位置如下:
在这里插入图片描述可以看到当前BeanDefinitionMap中注册了nameBookBeanDefinition,其beanClass指向的FactoryBean实现类BookFactory

refresh()调用finishBeanFactoryInitialization(beanFactory);的最后会调用DefaultListableBeanFactory#preInstantiateSingletons()方法来对BeanFactory中注册的所有BeanDefinition进行初始化:
在这里插入图片描述
如上图可以看到最外层循环所有注册的BeanDefinition的代码有区别BeanName是否是FactoryBean来走不同的逻辑。

看下isFactoryBean()的方法是如何判断其为FactoryBean的:
org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String)
在这里插入图片描述

org.springframework.beans.factory.support.AbstractBeanFactory#isFactoryBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition)
在这里插入图片描述
然后是对FactoryBean的实例化关键点,由于前面对FactoryBeanname加了"&“前缀,那么注册到BeanFactory中的单例Beankey是否也是加了”&"符号的呢?
继续看下:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
在这里插入图片描述
上面的第一步transformedBeanName(name)方法会将传入的name转成常规name,即:如果有"&“前缀则将”&"前缀去除。
然后对去除了&的name进行初始化,因为只有这样才能通过转成了常规的name找到BeanDefinition

org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance
在这里插入图片描述
经过以上过程,最终会将FactoryBean对象注册到BeanFactorysingletonObjects属性中,并且name为设置的id值,并不带&符号;
在这里插入图片描述

获取FactoryBean实际生产对象过程

那么问题来了,由于singletonObjects里面注册的BeanFactoryBean对象,并不是实际的bean,那么Spring是怎么调用的是FactoryBeangetObject方法返回的呢?
换句话说,Spring用FactoryBean生产出实际bean对象的过程是怎么样的呢?

我们从getBean方法开始:
在这里插入图片描述
一路Debug到下面doGetBean
在这里插入图片描述
由于FactoryBean在容器初始化时就已经实例化注册到了BeanFactory中,所以Object sharedInstance = getSingleton(beanName);这行代码是可以找到FactoryBean对象的。

关键在上图的bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);这行代码的调用。
由于是获取FactoryBean的实际Bean,所以是没有BeanDefinition的,所以传入的实参是null。
进入看下实现:
在这里插入图片描述
由于传入的name不是以&符号开头,所以获取的是FactoryBean生产的对象,首先是从factoryBeanObjectCache缓存中通过beanName获取,如果获取到则直接返回,如果获取不到,则调用object = getObjectFromFactoryBean(factory, beanName, !synthetic);这行代码获取生产的实际对象。

org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean
从上图的过程可以看到factoryisSingleton返回的是true的话,那么会首先从factoryBeanObjectCache中获取,如果缓存中获取不到则调用doGetObjectFromFactoryBean方法来实际调用FactoryBeangetObject来获取对象,并将对象放入到factoryBeanObjectCache中用于后续的直接获取。
如果是非单例的话,则直接调用FactoryBeangetObject来每次都新创建对象返回。

返回实际生产对象:
org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
在这里插入图片描述
最终在BeanFactory中呈现的内存情况:
在这里插入图片描述
可以看到工厂生产出来的实际对象存放在factoryBeanObjectCache变量中,工厂对象在容器初始化时注册在singletonObjects中;
并且两个变量中的key是相同的;

总结

  • SpringFactoryBean的实现关键在FactoryBeanRegistrySupport类,FactoryBeanRegistrySupport提供了一系列对FactoryBean的支持,其内部维护了一个factoryBeanObjectCache变量作为FactoryBean生产的单例bean的支持。
  • FactoryBean在容器初始化时就会被实例化到BeanFactorysingletonObjects中,FactoryBeangetObject生产出的bean如果是单例,则会存放到factoryBeanObjectCache变量中,便于后续的直接获取。
  • 通过getBean("name")获取的是FactoryBean生产的实际bean对象,通过getBean("&name")获取的是FactoryBean自身对象;

注意需要再配置<bean>的时候加上id值,没有加id值是不能默认通过首字母小写的类名获取到FactoryBean

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:04:09  更:2022-05-05 11:07:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:08:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码