Spring Framework 组件注册 之 FactoryBean
前言
前两篇文章介绍了如何使用@Component,@Import注解来向spring容器中注册组件(javaBean),本文将介绍通过FactoryBean接口继续向spring容器中注册组件。可能第一印象是spring中BeanFactory接口,但是这里确实说的是FactoryBean。
推荐阅读
Spring Framework 组件注册 之 @Import
Spring Framework 组件注册 之 @Component
FactoryBean 与 BeanFactory根据接口名称,我们也可以简单看出两者的区别
FactoryBean:它是spring中的一个Bean,只不过它是一个特殊的Bean(工厂Bean),我们可以通过它来自定义生产需要的普通JavaBean
BeanFactory:它是spring的Bean工厂,是spring最为重要的接口之一,spring通过此接口获取,管理容器中的各个Bean
接下来将进入本文正题,如何通过FactoryBean接口向spring容器中注册组件
FactoryBean简单使用正如前面说的,FactoryBean也是spring中的一个Bean,但是它又是一个特殊的Bean,它的存在是为了生产其他的JavaBean。首先我们看看FactoryBean自身的接口定义
public interface FactoryBean<T> { /** * 从Spring容器中获取Bean时会调用此方法,返回一个T对象 */ @Nullable T getObject() throws Exception; /** * 此工厂Bean返回对象的类型 */ @Nullable Class<?> getObjectType(); /** * 工厂Bean创建的对象是否为单例, * 如果返回false,说明getObject方法的实例对象不是单例的, * Spring每次从容器中获取T对象时,都调用getObject方法创建一个对象 */ default boolean isSingleton() { //spring 5 接口默认返回true(单例) return true; } }FactoryBean接口定义简单明了,就是用来获取一个Bean的基本信息,下面我们自己实现该接口,来生产一个javaBean
/** * 产生 Bike 对象的工厂Bean */ @Component public class BikeFactoryBean implements FactoryBean<Bike> { public Bike getObject() throws Exception { System.out.println("......开始创建Bike对象......"); return new Bike(); } public Class<?> getObjectType() { return Bike.class; } public boolean isSingleton() { return true; } }自定义的一个JavaBean类
/** * 待注册的自定义组件 */ @Data public class Bike { private String id = "by FactoryBean"; }添加spring容器启动的引导类
/** * spring 容器启动引导类,测试 FactoryBean 功能 */ @ComponentScan("com.spring.study.ioc.factorybean") public class TestFactoryBeanBootstrap { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestFactoryBeanBootstrap.class); //获取工厂Bean本身的Id String[] beanNames = applicationContext.getBeanNamesForType(BikeFactoryBean.class); System.out.println("BikeFactoryBean names:" + Arrays.asList(beanNames)); //获取工厂Bean产生的Bean的Id beanNames = applicationContext.getBeanNamesForType(Bike.class); System.out.println("Bike bean names:" + Arrays.asList(beanNames)); Object bean = applicationContext.getBean("bikeFactoryBean"); System.out.println(bean); bean = applicationContext.getBean(Bike.class); System.out.println(bean); // 获取工厂Bean 本身的实例对象 bean = applicationContext.getBean(BeanFactory.FACTORY_BEAN_PREFIX + "bikeFactoryBean"); System.out.println(bean); applicationContext.close(); } }启动spring容器,控制台打印结果:
BikeFactoryBean names:[&bikeFactoryBean]
Bike bean names:[bikeFactoryBean]
......开始创建Bike对象......
Bike(id=by FactoryBean)
Bike(id=by FactoryBean)
com.spring.study.ioc.factorybean.BikeFactoryBean@4eb7f003
由结果可以看出
虽然代码中只在BikeFactoryBean类上加了@Component注解,但是从spring容器仍然可以获取到Bike类的信息
工厂Bean的Id与实际产生的Bean的Id仅差了一个&符,也就是说,工厂Bean定义的Id实际为getObject()方法返回Bean的Id,而工厂Bean本身的Id被添加了一个前缀&符
工厂Bean的isSingleton()方法返回了true,所以通过spring容器多次获取实际的Bean时,getObject()方法也是执行一次
根据工厂Bean的Id可以看出,要想从spring容器中获取工厂Bean本身,则需要在注册的Id前面添加一个&符,而此前缀在BeanFactory接口中已经定义了FACTORY_BEAN_PREFIX
如果将BikeFactoryBean的isSingleton()方法返回了false
public boolean isSingleton() { return false; }重新启动spring容器,可以看如下结果:
BikeFactoryBean names:[&bikeFactoryBean]
Bike bean names:[bikeFactoryBean]
......开始创建Bike对象......
Bike(id=by FactoryBean)
......开始创建Bike对象......
Bike(id=by FactoryBean)
com.spring.study.ioc.factorybean.BikeFactoryBean@4eb7f003
由结果可以看出,唯一的变化在于从spring容器中多次获取实际Bean时,工厂Bean的getObject()方法被多次进行了调用。这与spring容器中被标识为原型的普通Bean相同,每次从spring中获取Bean时都会被实例化。
FactoryBean 执行过程