在我们的开发工作中应该都见过或使用过FactoryBean这个类,也许你会看成了BeanFactory这个类。FactoryBean和BeanFactory虽然长的很像,但是他们的作用确实完全不像。这里你可以想象一下,你会在什么样的场景下使用FactoryBean这个接口?FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。BeanFactory是Spring容器中的一个基本类也是很重要的一个类,在BeanFactory中可以创建和管理Spring容器中的Bean,它对于Bean的创建有一个统一的流程。下面我们先看一下FactoryBean中有什么东西:

public interface FactoryBean<T> {//返回的对象实例T getObject() throws Exception;//Bean的类型Class<?> getObjectType();//true是单例,false是非单例  在Spring5.0中此方法利用了JDK1.8的新特性变成了default方法,返回trueboolean isSingleton();
}

从上面的代码中我们发现在FactoryBean中定义了一个Spring Bean的很重要的三个特性:是否单例、Bean类型、Bean实例,这也应该是我们关于Spring中的一个Bean最直观的感受。虽然没有返回BeanName的值,但是我们也知道BeanName的值。下面我们来写一个关于FactoryBean的小例子,看看我们是怎么使用FactoryBean的,然后再从源码的角度看看Spring是怎么解析FactoryBean中的Bean的。

//FactoryBean接口的实现类
@Component
public class FactoryBeanLearn implements FactoryBean {@Overridepublic Object getObject() throws Exception {//这个Bean是我们自己new的,这里我们就可以控制Bean的创建过程了return new FactoryBeanServiceImpl();}@Overridepublic Class<?> getObjectType() {return FactoryBeanService.class;}@Overridepublic boolean isSingleton() {return true;}
}
//接口
public interface FactoryBeanService {/*** 测试FactoryBean*/void testFactoryBean();
}
//实现类
public class FactoryBeanServiceImpl implements FactoryBeanService {/*** 测试FactoryBean*/@Overridepublic void testFactoryBean() {System.out.println("我是FactoryBean的一个测试类。。。。");}
}
//单测
@Test
public void test() {ClassPathXmlApplicationContext cac = new ClassPathXmlApplicationContext("classpath:com/zkn/spring/learn/base/applicationContext.xml");FactoryBeanService beanService = cac.getBean(FactoryBeanService.class);beanService.testFactoryBean();}

我们的输出结果如下:

从上面的代码中我们可以看到我们从Spring容器中获取了FactoryBeanService类型的Bean。那么这个获取Bean的过程Spring是怎么处理的呢?它是怎么从FactoryBean中获取我们自己创建的Bean实例的呢?我们先从getBean这个方法看起,因为在Spring的AbstractApplicationContext中有很多重载的getBean方法,这里我们调用的是根据Type(指Class类型)来获取的Bean信息。我们传入的type是FactoryBeanService类型。

getBean

AbstractApplicationContext#getBean(java.lang.Class)

    @Overridepublic <T> T getBean(Class<T> requiredType) throws BeansException {//检测BeanFactory的激活状态assertBeanFactoryActive();//getBeanFactory()获取到的是一个DefaultListableBeanFactory的实例//所以我们去DefaultListableBeanFactory中看一下getBean这个方法return getBeanFactory().getBean(requiredType);}

DefaultListableBeanFactory#getBean(java.lang.Class)

    @Overridepublic <T> T getBean(Class<T> requiredType) throws BeansException {return getBean(requiredType, (Object[]) null);}@Overridepublic <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {//解析BeanNamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args);if (namedBean != null) {return namedBean.getBeanInstance();}//如果当前Spring容器中没有获取到相应的Bean信息,则从父容器中获取//SpringMVC是一个很典型的父子容器BeanFactory parent = getParentBeanFactory();if (parent != null) {//一个重复的调用过程,只不过BeanFactory的实例变了return parent.getBean(requiredType, args);}//如果都没有获取到,则抛出异常throw new NoSuchBeanDefinitionException(requiredType);}

在上面的代码中,我们重点关注的是resolveNamedBean这个方法:

    private <T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType, Object... args) throws BeansException {Assert.notNull(requiredType, "Required type must not be null");//这个方法是根据传入的Class类型来获取BeanName,因为我们有一个接口有多个实现类的情况(多态),//所以这里返回的是一个String数组。这个过程也比较复杂。//这里需要注意的是,我们调用getBean方法传入的type为com.zkn.spring.learn.service.FactoryBeanService类型,但是我们没有在Spring容器中注入FactoryBeanService类型的Bean//正常来说我们在这里是获取不到beanName呢。但是事实是不是这样呢?看下面我们对getBeanNamesForType的分析String[] candidateNames = getBeanNamesForType(requiredType);//如果有多个BeanName,则挑选合适的BeanNameif (candidateNames.length > 1) {List<String> autowireCandidates = new ArrayList<String>(candidateNames.length);for (String beanName : candidateNames) {if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {autowireCandidates.add(beanName);}}if (!autowireCandidates.isEmpty()) {candidateNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);}}//如果只有一个BeanName 我们调用getBean方法来获取Bean实例来放入到NamedBeanHolder中//这里获取bean是根据beanName,beanType和args来获取bean//这里是我们要分析的重点 在下一篇文章中有介绍if (candidateNames.length == 1) {String beanName = candidateNames[0];return new NamedBeanHolder<T>(beanName, getBean(beanName, requiredType, args));}//如果合适的BeanName还是有多个的话else if (candidateNames.length > 1) {Map<String, Object> candidates = new LinkedHashMap<String, Object>(candidateNames.length);for (String beanName : candidateNames) {//看看是不是已经创建多的单例Beanif (containsSingleton(beanName)) {candidates.put(beanName, getBean(beanName, requiredType, args));}else {//调用getType方法继续获取Bean实例candidates.put(beanName, getType(beanName));}}//有多个Bean实例的话 则取带有Primary注解或者带有Primary信息的BeanString candidateName = determinePrimaryCandidate(candidates, requiredType);if (candidateName == null) {//如果没有Primary注解或者Primary相关的信息,则去优先级高的Bean实例candidateName = determineHighestPriorityCandidate(candidates, requiredType);}if (candidateName != null) {Object beanInstance = candidates.get(candidateName);//Class类型的话 继续调用getBean方法获取Bean实例if (beanInstance instanceof Class) {beanInstance = getBean(candidateName, requiredType, args);}return new NamedBeanHolder<T>(candidateName, (T) beanInstance);}//都没有获取到 抛出异常throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());}return null;}

在上面的代码中我们说我们传入的type是com.zkn.spring.learn.service.FactoryBeanService类型,但是在我们的Spring容器中却没有FactoryBeanService类型的Bean,那么我们是怎么从getBeanNamesForType获取到beanName的呢?
getBeanNamesForType的分析

    public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {if (!isConfigurationFrozen() || type == null || !allowEagerInit) {return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);}//先从缓存中获取Map<Class<?>, String[]> cache =(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);String[] resolvedBeanNames = cache.get(type);if (resolvedBeanNames != null) {return resolvedBeanNames;}//调用doGetBeanNamesForType方法获取beanNameresolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);//所传入的类能不能被当前类加载加载if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {//放入到缓存中,解析一次以后从缓存中获取//这里对应到我们这里 key是FactoryBeanService Value是beanFactoryLearncache.put(type, resolvedBeanNames);}return resolvedBeanNames;}

doGetBeanNamesForType的分析

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {List<String> result = new ArrayList<String>();//循环所有的beanName 这个是在Spring容器启动解析Bean的时候放入到这个List中的for (String beanName : this.beanDefinitionNames) {//不是别名if (!isAlias(beanName)) {try {//根据beanName获取RootBeanDefinitionRootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);//RootBeanDefinition中的Bean不是抽象类、非延迟初始化 if (!mbd.isAbstract() && (allowEagerInit ||((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&!requiresEagerInitForType(mbd.getFactoryBeanName()))) {//是不是FactoryBean的子类boolean isFactoryBean = isFactoryBean(beanName, mbd);BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();//这里我们其他的几个变量的意思都差不多  我们需要重点关注的是isTypeMatch这个方法 //如果isTypeMatch这个方法返回true的话,我们会把这个beanName即factoryBeanLearn 放入到result中返回boolean matchFound =(allowEagerInit || !isFactoryBean ||(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&(includeNonSingletons ||(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&isTypeMatch(beanName, type);if (!matchFound && isFactoryBean) {//如果不匹配,还是FactoryBean的子类 这里会把beanName变为 &beanNamebeanName = FACTORY_BEAN_PREFIX + beanName;//判断类型匹配不匹配matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);}if (matchFound) {result.add(beanName);}}}}}//这里的Bean是Spring容器创建的特殊的几种类型的Bean 像Environmentfor (String beanName : this.manualSingletonNames) {try {// In case of FactoryBean, match object created by FactoryBean.if (isFactoryBean(beanName)) {if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {result.add(beanName);// Match found for this bean: do not match FactoryBean itself anymore.continue;}// In case of FactoryBean, try to match FactoryBean itself next.beanName = FACTORY_BEAN_PREFIX + beanName;}// Match raw bean instance (might be raw FactoryBean).if (isTypeMatch(beanName, type)) {result.add(beanName);}}}return StringUtils.toStringArray(result);}

在上面的代码中,我们可以知道的是我们的FactoryBeanLearn是一个FactoryBean类型的类。所以在上面的代码中会调用isTypeMatch这个方法来判断FactoryBeanLearn是不是和我们传入的类型相匹配。这里是值:FactoryBeanService类。我们去isTypeMatch方法中去看一下它是怎么进行类型匹配判断的:由于isTypeMatch方法很长,这里我们只看和我们这次分析相关的一部分代码

    public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {//转换beanName   这里我们可以知道我们的beanName为factoryBeanLearn 因为上面是循环了Spring容器中的所有的BeanString beanName = transformedBeanName(name);//因为我们这里是用的AbstractApplicationContext的子类来从Spring容器中获取Bean//获取beanName为factoryBeanLearn的Bean实例 这里是可以获取到Bean实例的//这里有一个问题:使用AbstractApplicationContext的子类从Spring容器中获取Bean和//使用BeanFactory的子类从容器中获取Bean有什么区别?这个可以思考一下Object beanInstance = getSingleton(beanName, false);if (beanInstance != null) {//factoryBeanLearn是FactoryBean的一个实现类if (beanInstance instanceof FactoryBean) {//这里判断beanName是不是以&开头  这里明显不是 这里可以想一下什么情况下会有&开头的Beanif (!BeanFactoryUtils.isFactoryDereference(name)) {//这里就是从factoryBeanLearn中获type类型 我们在下面会分析一下这个类Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);//从factoryBeanLearn中获取到的type类型和我们传入的类型是不是同一种类型 是的话直接返回return (type != null && typeToMatch.isAssignableFrom(type));}else {return typeToMatch.isInstance(beanInstance);}}

getTypeForFactoryBean

    protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {try {if (System.getSecurityManager() != null) {return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {@Overridepublic Class<?> run() {return factoryBean.getObjectType();}}, getAccessControlContext());}else {//看到这里是不是很熟悉了 调用FactoryBean实例的getObjectType()方法return factoryBean.getObjectType();}}}

我们在调用factoryBeanLearn的getObjectType方法的时候,获取到的值为:com.zkn.spring.learn.service.FactoryBeanService和我们传入的type是一样的类型。所以这里返回true,根据我们上面说的如果isTypeMatch返回true的话,我们返回的beanName为factoryBeanLearn
上面的分析总结起来是:我们调用getBean(Class requiredType)方法根据类型来获取容器中的bean的时候,对应我们的例子就是:根据类型com.zkn.spring.learn.service.FactoryBeanService来从Spring容器中获取Bean(首先明确的一点是在Spring容器中没有FactoryBeanService类型的BeanDefinition。但是却有一个Bean和FactoryBeanService这个类型有一些关系)。Spring在根据type去获取Bean的时候,会先获取到beanName。获取beanName的过程是:先循环Spring容器中的所有的beanName,然后根据beanName获取对应的BeanDefinition,如果当前bean是FactoryBean的类型,则会从Spring容器中根据beanName获取对应的Bean实例,接着调用获取到的Bean实例的getObjectType方法获取到Class类型,判断此Class类型和我们传入的Class是否是同一类型。如果是则返回测beanName,对应到我们这里就是:根据factoryBeanLearn获取到FactoryBeanLearn实例,调用FactoryBeanLearn的getObjectType方法获取到返回值FactoryBeanService.class。和我们传入的类型一致,所以这里获取的beanName为factoryBeanLearn。换句话说这里我们把factoryBeanLearn这个beanName映射为了:FactoryBeanService类型。即FactoryBeanService类型对应的beanName为factoryBeanLearn这是很重要的一点。在这里我们也看到了FactoryBean中三个方法中的一个所发挥作用的地方。我们在下一篇文章中着重分析:getBean(String name, Class requiredType, Object… args)这个方法。

Spring系列之FactoryBean(一)相关推荐

  1. 深入理解Spring系列之六:bean初始化

    <深入理解Spring系列之四:BeanDefinition装载前奏曲>中提到,对于非延迟单例bean的初始化在finishBeanFactoryInitialization(beanFa ...

  2. Spring 系列: Spring 框架简介

    Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架. 在这篇由三部 ...

  3. 深入理解Spring系列之三:BeanFactory解析

    转载 https://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483824&idx=1&sn=9b7c2603093 ...

  4. spring系列-注解驱动原理及源码-bean组件注册

    目录 一.环境初始化 1.环境准备 二.bean的手动注入 1.xml方式注入bean 2.使用@Configuration&@Bean方式注入bean 三.自动扫描注册组件及bean 1.使 ...

  5. Spring 系列: Spring 框架

    第一部分:Spring 框架简介 Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序 ...

  6. 手撸Spring系列2:IOC/DI 思想(源码篇-IOC)

    说在前头: 笔者本人为大三在读学生,书写文章的目的是为了对自己掌握的知识和技术进行一定的记录,同时乐于与大家一起分享,因本人资历尚浅,发布的文章难免存在一些错漏之处,还请阅读此文章的大牛们见谅与斧正. ...

  7. Spring系列中文文档

    Spring系列中文文档 Spring源码 中文手册 Spring Core上 Spring Core下 Spring容器扩展点(Container Extension Points) Factory ...

  8. @autowired注解_品Spring:对@Autowired和@Value注解的处理方法(文末附spring系列资源合集)...

    作者:编程新说李新杰 出自:微信公众号"编程新说" 原文:品Spring:对@Autowired和@Value注解的处理方法 在Spring中能够完成依赖注入的注解有JavaSE提 ...

  9. Spring 系列,第 2 部分: 当 Hibernate 遇上 Spring

    为什么80%的码农都做不了架构师?>>>    在这个系列的 前一期中,我介绍了 Spring 框架的 7 个模块,包括 Spring AOP 和控制反转(IOC)容器.然后我用一个 ...

最新文章

  1. Insertion Loss Return Loss
  2. 第七章 控制PL/SQL错误
  3. mysql 双冒号_jdk8新特性之双冒号 :: 用法及详解
  4. python中进制chr_python中的chr() 如何返回字符?
  5. 舍本求末的运维自动化技术热潮
  6. 【SpringBoot 2】(五)自动配置简析源码 开发中小技巧
  7. mac mail 删除邮件服务器,如何从Mac OS X中的邮件中删除所有电子邮件 | MOS86
  8. Marco:Filecash全网算力增加的趋势,将形成FIC价格上升的良性循环
  9. 单点登录(Single Sign On)
  10. Java 游戏自动寻路,老游戏仙境传说RO私服自动寻路求交流(含代码)
  11. 飞鸽快递系统代码_中通快递港股上市募资约96亿港元,董事长赖梅松认为股票代码2057寓意开启新征程...
  12. 使用python对目录下的文件进行分类
  13. 02.集线器,网桥,交换机
  14. erp仓储管理 java,关于java:ERP仓库管理的操作与设计开源软件诞生20
  15. 论文笔记之Stein变分梯度下降
  16. 【gdgzezoi】Problem B: 天天爱跑步
  17. led灯条维修_LED灯坏了怎么维修?学会自己动手,简单的LED故障自己也能维修
  18. java注解检验集合对象_Java 对list对象进行属性校验
  19. GRE词汇统计大全(一)
  20. 循环渐进NsDoor(一)

热门文章

  1. Linux ❉ top命令详解
  2. python获取小时和分钟_Python:以小时,分钟和秒为单位读取文本文件;和度数,弧分和弧秒...
  3. 新闻|花旗银行、阿联酋阿布札比投资局来访智链万源
  4. 2022年水彩笔市场前景分析及研究报告
  5. java开发环境(java开发环境和运行环境)
  6. 2023年全国最新会计专业技术资格精选真题及答案52
  7. 操作系统原理实验-进程同步
  8. 富友支付接口对接不是必填的值如何处理
  9. 一分钟了解Java Attach机制
  10. HDU 5172 GTY's gay friends HASH随机算法