Bootstrap

【Spring学习】FactoryBean的使用

前言

FactoryBean 接口对于 Spring 框架来说占有重要的地位,Spring 自身就提供了 70 多个FactoryBean 的实现。它们隐藏了实例化一些复杂 bean 的细节,给上层应用带来了便利。

一、FactoryBean介绍

1、Spring为什么使用FactoryBean

一般情况下,Spring 通过反射机制利用 bean 的 class 属性指定实现类来实例化 bean

在某些情况下,实例化 bean 过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。

Spring 为此提供了一个 org.Springframework.bean.factory.FactoryBean 的工厂类接口,用户可以通过实现该接口定制实例化 bean 的逻辑

2、FactoryBean接口

2.1 接口定义

从Spring3.0开始,FactoryBean 开始支持泛型,即接口声明改为 FactoryBean< T> 的形式:

package org.springframework.beans.factory;

import org.springframework.lang.Nullable;


public interface FactoryBean<T> {

	
	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";

	@Nullable
	T getObject() throws Exception;

	@Nullable
	Class<?> getObjectType();

	default boolean isSingleton() {
		return true;
	}

}

2.2 方法说明

  • T getObject():返回由 FactoryBean 创建的 bean 实例,如果 isSingleton()返回 true,则该实例会放到 Spring 容器中单实例缓存池中。
  • boolean isSingleton():返回由 FactoryBean 创建的 bean 实例的作用域是 singleton 还是prototype。
  • Class getObjectType():返回 FactoryBean 创建的 bean 类型。

3、FactoryBean在Spring中的作用

当配置文件中< bean>的 class 属性配置的实现类是 FactoryBean 时,通过 getBean()方法返回的不是 FactoryBean 本身,而是 FactoryBean#getObject() 方法所返回的对象。相当于FactoryBean#getObject()代理了 getBean()方法。

二、FactoryBean使用

现在,让我们通过demo的形式演示FactoryBean如何使用吧。

目录结构如下:
在这里插入图片描述

1、定义POJO

package com.cms.factorybean;

import lombok.Data;

/**
 * @author: coffee
 * @date: 2023/3/5 7:04 PM
 * @description: ...
 */
@Data
public class Car {

    private String color;

    private String maxSpeed;

    private String price;

    @Override
    public String toString() {
        return "Car{" +
                "color='" + color + '\'' +
                ", maxSpeed='" + maxSpeed + '\'' +
                ", price='" + price + '\'' +
                '}';
    }
}

2、实现FactoryBean

package com.cms.factorybean;

import org.springframework.beans.factory.FactoryBean;

/**
 * @author: coffee
 * @date: 2023/3/5 7:07 PM
 * @description: 通过逗号分隔符的方式一次性为Car的所有属性指定配置值。
 */
public class CarFactoryBean implements FactoryBean<Car> {

    private String carInfo;

    @Override
    public Car getObject() throws Exception {
        Car car = new Car();
        String[] infoArr = carInfo.split(",");
        car.setColor(infoArr[0]);
        car.setPrice(infoArr[1]);
        car.setMaxSpeed(infoArr[2]);
        return car;
    }

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

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

    /**
     * 功能描述:给carInfo属性注入指定值。(xml方式注入值)
     *
     * @param carInfo 值
     */
    public void setCarInfo(String carInfo) {
        this.carInfo = carInfo;
    }
}

3、配置xml文件

<?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 id = "car" class="com.cms.factorybean.CarFactoryBean" >
        <property name="carInfo" value="红色,100,200"/>
    </bean>

</beans>

4、测试类

package com.cms.factorybean;

import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author: cms
 * @date: 2023/3/3 3:19 PM
 * @description: xxx
 */
public class FactoryBeanTest {

    @Test
    public void test () {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/cms/factorybean/car.xml");

        // 1.当调用 getBean("car")时,Spring 通过反射机制发现 CarFactoryBean 实现了 FactoryBean的接口,这时 Spring 容器就调用接口方法 CarFactoryBean#getObject()方法返回。
        Car car = (Car) context.getBean("car");
        // 输出:Car{color='红色', maxSpeed='200', price='100'}
        System.out.println(car);
        
        //2.如果希望获取CarFactoryBean 的实例,则需要在使用 getBean(beanName)方法时在 beanName 前显示的加上"&”前缀,例如 getBean("&car")。
        CarFactoryBean carFactoryBean = (CarFactoryBean)context.getBean("&car");
        // 输出:com.cms.factorybean.CarFactoryBean@7748410a
        System.out.println(carFactoryBean.toString());
    }
}

结果:

08:00:41.209 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2b6856dd
08:00:41.322 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [com/cms/factorybean/car.xml]
08:00:41.346 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'car'
Car{color='红色', maxSpeed='200', price='100'}
com.cms.factorybean.CarFactoryBean@7748410a

Process finished with exit code 0

三、getBean(“&car”)与getBean(“car”)

单例池存储内容

在这里插入图片描述

getBean(“car”)

在这里插入图片描述
源码debug:
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

getBean(“&car”)

getBean(“&car”)返回的是factorybean对象:
在这里插入图片描述

源码debug:
在这里插入图片描述在这里插入图片描述

四、源码位置

【Gitee】spring-xml-demo模块

总结

待补充。

;