写在前面

在前面的文章中,我们知道可以通过多种方式向Spring容器中注册bean。可以使用@Configuration结合@Bean向Spring容器中注册bean;可以按照条件向Spring容器中注册bean;可以使用@Import向容器中快速导入bean对象;可以在@Import中使用ImportBeanDefinitionRegistrar向容器中注册bean。

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

FactoryBean概述

一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean 。在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在标签中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可以得到一个更加简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑。

FactoryBean接口对于Spring框架来说占有重要的地位,Spring 自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。从Spring 3.0 开始, FactoryBean开始支持泛型,即接口声明改为FactoryBean 的形式:

在Spring 5.2.6版本中,FactoryBean接口的定义如下所示。

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

这里,需要注意的是:当配置文件中标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。

FactoryBean实例

首先,创建一个PersonFactoryBean,实现FactoryBean接口,如下所示。

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.factory.FactoryBean;
/**
* @author binghe
* @version 1.0.0
* @description 商品的FactoryBean,测试FactoryBean
*/
public class PersonFactoryBean implements FactoryBean<Person> { //返回一个Person对象,这个对象会被注册到Spring容器中
@Override
public Person getObject() throws Exception {
return new Person();
} @Override
public Class<?> getObjectType() {
return Person.class;
} //bean是否为单例;true:是;false:否
@Override
public boolean isSingleton() {
return true;
}
}

接下来,我们在PersonConfig2类中加入PersonFactoryBean的声明,如下所示。

@Bean
public PersonFactoryBean personFactoryBean(){
return new PersonFactoryBean();
}

这里需要小伙伴们注意的是:我在这里使用@Bean注解向Spring容器中添加的是PersonFactory对象。那我们就来看看Spring容器中有哪些bean。接下来,运行SpringBeanTest类中的testAnnotationConfig7()方法,输出的结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company

可以看到,结果信息中输出了一个personFactoryBean,我们看下这个personFactoryBean到底是个什么鬼!此时,我们对SpringBeanTest类中的testAnnotationConfig7()方法稍加改动,添加获取personFactoryBean的代码,并输出personFactoryBean实例的类型,如下所示。

@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println); Object personFactoryBean = context.getBean("personFactoryBean");
System.out.println("personFactoryBean实例的类型为:" + personFactoryBean.getClass());
}

再次运行SpringBeanTest类中的testAnnotationConfig7()方法,输出的结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
personFactoryBean实例的类型为:class io.mykit.spring.plugins.register.bean.Person

可以看到,虽然我在代码中使用@Bean注解注入的PersonFactoryBean对象,但是,实际上从Spring容器中获取到的bean对象却是调用PersonFactoryBean类中的getObject()获取到的Person对象。

看到这里,是不是有种豁然开朗的感觉!!!

在PersonFactoryBean类中,我们将Person对象设置为单实例bean,接下来,我们在SpringBeanTest类中的testAnnotationConfig7()方法多次获取Person对象,并输出多次获取的对象是否为同一对象,如下所示。

@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println); Object personFactoryBean1 = context.getBean("personFactoryBean");
Object personFactoryBean2 = context.getBean("personFactoryBean");
System.out.println(personFactoryBean1 == personFactoryBean2);
}

运行testAnnotationConfig7()方法输出的结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
true

可以看到,在PersonFactoryBean类的isSingleton()方法中返回true时,每次获取到的Person对象都是同一个对象,说明Person对象是单实例bean。

这里,可能就会有小伙伴要问了,如果将Person对象修改成多实例bean呢?别急,这里我们只需要在PersonFactoryBean类的isSingleton()方法中返回false,即可将Person对象设置为多实例bean,如下所示。

//bean是否为单例;true:是;false:否
@Override
public boolean isSingleton() {
return false;
}

再次运行SpringBeanTest类中的testAnnotationConfig7()方法,输出的结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
false

可以看到,最终结果返回了false,说明此时Person对象是多实例bean。

如何在Spring容器中获取到FactoryBean对象?

之前,我们使用@Bean注解向Spring容器中注册的PersonFactoryBean,获取出来的确实Person对象。那么,小伙伴们可能会问:我就想获取PersonFactoryBean实例,该怎么办呢?

其实,这也很简单, 只需要在获取bean对象时,在id前面加上&符号即可

打开我们的测试类SpringBeanTest,在testAnnotationConfig7()方法中添加获取PersonFactoryBean实例的代码,如下所示。

@Test
public void testAnnotationConfig7(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig2.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println); Object personFactoryBean1 = context.getBean("personFactoryBean");
Object personFactoryBean2 = context.getBean("personFactoryBean");
System.out.println("personFactoryBean1类型:" + personFactoryBean1.getClass());
System.out.println("personFactoryBean2类型:" + personFactoryBean2.getClass());
System.out.println(personFactoryBean1 == personFactoryBean2); Object personFactoryBean3 = context.getBean("&personFactoryBean");
System.out.println("personFactoryBean3类型:" + personFactoryBean3.getClass());
}

运行SpringBeanTest类中的testAnnotationConfig7()方法,输出的结果信息如下所示。

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig2
io.mykit.spring.plugins.register.bean.Department
io.mykit.spring.plugins.register.bean.Employee
io.mykit.spring.plugins.register.bean.User
io.mykit.spring.plugins.register.bean.Role
person
binghe001
personFactoryBean
company
personFactoryBean1类型:class io.mykit.spring.plugins.register.bean.Person
personFactoryBean2类型:class io.mykit.spring.plugins.register.bean.Person
false
personFactoryBean3类型:class io.mykit.spring.plugins.register.bean.PersonFactoryBean

可以看到,在获取bean时,在id前面加上&符号就会获取到PersonFactoryBean实例对象。

那问题又来了!!为什么在id前面加上&符号就会获取到PersonFactoryBean实例对象呢?

接下来,我们就揭开这个神秘的面纱,打开BeanFactory接口,

package org.springframework.beans.factory;
import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable; public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
/**************以下省略n行代码***************/
}

看到这里,是不是明白了呢?没错,在BeanFactory接口中定义了一个&前缀,只要我们使用bean的id来从Spring容器中获取bean时,Spring就会知道我们是在获取FactoryBean本身。

好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!

项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。

【String注解驱动开发】面试官让我说说:如何使用FactoryBean向Spring容器中注册bean?的更多相关文章

  1. 【String注解驱动开发】如何按照条件向Spring容器中注册bean?这次我懂了!!

    写在前面 当bean是单实例,并且没有设置懒加载时,Spring容器启动时,就会实例化bean,并将bean注册到IOC容器中,以后每次从IOC容器中获取bean时,直接返回IOC容器中的bean,不 ...

  2. 【Spring注解驱动开发】在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean

    写在前面 在前面的文章中,我们学习了如何使用@Import注解向Spring容器中导入bean,可以使用@Import注解快速向容器中导入bean,小伙伴们可以参见<[Spring注解驱动开发] ...

  3. 【String注解驱动开发】困扰了我很久的AOP嵌套调用终于解决了!

    写在前面 最近在分析Spring源码时,在同一个类中写了嵌套的AOP方法,测试时出现:Spring AOP在同一个类里自身方法相互调用时无法拦截.哎,怎么办?还能怎么办呢?继续分析Spring源码,解 ...

  4. 【String注解驱动开发】你了解@PostConstruct注解和@PreDestroy注解吗?

    写在前面 在之前的文章中,我们介绍了如何使用@Bean注解指定初始化和销毁的方法,小伙伴们可以参见<[Spring注解驱动开发]如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!& ...

  5. 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件

    写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...

  6. 【Spring注解驱动开发】如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!

    写在前面 在[String注解驱动开发专题]中,前面的文章我们主要讲了有关于如何向Spring容器中注册bean的知识,大家可以到[String注解驱动开发专题]中系统学习.接下来,我们继续肝Spri ...

  7. 【Spring注解驱动开发】如何使用@Value注解为bean的属性赋值,我们一起吊打面试官!

    写在前面 在之前的文章中,我们探讨了如何向Spring的IOC容器中注册bean组件,讲解了有关bean组件的生命周期的知识.今天,我们就来一起聊聊@Value注解的用法. 项目工程源码已经提交到Gi ...

  8. 【Spring注解驱动开发】BeanPostProcessor在Spring底层是如何使用的?看完这篇我懂了!!

    写在前面 在<[String注解驱动开发]面试官再问你BeanPostProcessor的执行流程,就把这篇文章甩给他!>一文中,我们详细的介绍了BeanPostProcessor的执行流 ...

  9. 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则

    写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...

随机推荐

  1. 不可不知的辅助测试的Fiddler小技巧

    在以前的博文中,时常有分享Fiddler的一些使用技巧,今天再贴下. Fiddler抓包工具使用详解 利用Fiddler拦截接口请求并篡改数据 Fiddler使用过程中容易忽略的小技巧 Mock测试, ...

  2. parrot os 创建swap分区&swapon failed invalid argument解决

    parrot os(不仅仅是debian系统),分区提示,查看系统格式为btrfs,需要注意的是btrfs无法添加swap分区,但是可以在5.0内核以上添加 以下命令,完成创建8g的swap分区 to ...

  3. Python编程思想(3):数字及其相关运算

    Python 提供了三种数值类型:int(整型),float(浮点型)和complex(复数). int:通常被称为整型或者整数,如200.299.10都属于整型: float:浮点数包含整数和小数部 ...

  4. 数组API汇总

    数组API汇总   Javascript数组API: 1.将数组转化为字符串:2种: 1.var str=String(str); 将数组转化为字符串并分隔每个元素; var arr=[1,2,3]; ...

  5. Qcom rampdump解析工具使用

    使用如下命令获取qcom工具: ljj@ljj-ThinkCentre-M83:~/git/qcom_tools/ramdump$ git clone git://codeaurora.org/qui ...

  6. Java实现 蓝桥杯 算法提高 摩尔斯电码

    算法提高 9-3摩尔斯电码 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 摩尔斯电码破译.类似于乔林教材第213页的例6.5,要求输入摩尔斯码,返回英文.请不要使用"zy ...

  7. 「从零单排canal 01」 canal 10分钟入门(基于1.1.4版本)

    1.简介 canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据 订阅 和 消费.应该是阿里云DTS(Data Transfer Servi ...

  8. 如何拿到阿里P8 Offer-候选人视角谈面试

    自我介绍 首先简单自我介绍一下,我叫陈映平,花名叫做小卡,2011年校招进入腾讯,是腾讯课堂B侧的前端技术负责人.2015年响应总理的号召,跟朋友一起出来创业,跟前面一位讲师的经历有点像,然后2018 ...

  9. iOS-函数式编程 && 响应式编程概念

    作为一个iOS developer,那么你一定用过Masnory / SnapKit: Masonry是一个OC开发中,非常好用的自动布局的第三方框架: SnapKit是Masonry团队打造的swi ...

  10. 最新 iOS 框架整体梳理(二)

    在前面一篇中整理出来了一些了,下面的内容是接着上面一篇的接着整理.上篇具体的内容可以点击这里查看:   最新 iOS 框架整体梳理(一) Part - 2          34.CoreTeleph ...