Spring的FactoryBean接口的作用

发布时间 2023-12-10 11:55:17作者: 残城碎梦

FactoryBean是Spring提供的一个接口,通过实现这个接口可以定制实例化bean。

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

Spring为什么使用FactoryBean

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

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

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

FactoryBean接口

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

public interface FactoryBean<T> {

    T getObject() throws Exception;

    Class<?> getObjectType();

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

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

FactoryBean案例演示

定义POJO

import lombok.Data;

@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 + '\'' +
            '}';
    }
}

实现FactoryBean

import org.springframework.beans.factory.FactoryBean;

/**
 * @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;
    }
}

配置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.harvey.CarFactoryBean" >
        <property name="carInfo" value="红色,100,200"/>
    </bean>

</beans>

测试类

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

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());
    }
}

源码分析