Spring源码系列——FactoryBean源码浅谈

233 阅读4分钟

前言

在实际开发当中,如果偏向于系统基础架构开发的话,对于FactoryBean的使用应该不会特别陌生。FactoryBean和BeanFactory这二者经常会被拿出来比较,原因在于这二者的名字看起来很容易让人混淆,然而这二者的原理和作用完全不一样。本篇文章将围绕FactoryBean展开源码解析。

FactoryBean接口
public interface FactoryBean<T> {
	// 返回创建的bean对象
	T getObject() throws Exception;
    // bean对象的Class类型
    Class<?> getObjectType();
    // bean对象是否是单例,默认为单例。
    default boolean isSingleton() {
		return true;
	}
}

我们来写一个类测试一下,看看能够从容器中获取到MyDemoFactoryBean和MyDemoService这两个bean:

@Configuration
@ComponentScan("com.leon.factorybean")
public class Config {
}
public class MyApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
        String[] myDemoTactoryBeanNames = applicationContext.getBeanNamesForType(MyDemoFactoryBean.class);
        String[] myDemoServicBeanNames = applicationContext.getBeanNamesForType(MyDemoService.class);
        System.out.println(JsonUtil.convert(myDemoTactoryBeanNames));
        System.out.println(JsonUtil.convert(myDemoServicBeanNames));
    }
}

FactoryBean源码追踪

1.入口

在Spring当如,追踪源码的方式最直接的方式就是通过applicatonContext.getBean方式。

MyDemoFactoryBean myDemoFactoryBean = applicationContext.getBean(MyDemoFactoryBean.class);

2.委托给AbstractApplicationContext#getBean

实际调用的是AbstractApplicationContext#getBean(java.lang.Class),该方法其实也是空壳方法,实际将其委托给BeanFactory的getBean方法。

@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
    assertBeanFactoryActive();
    return getBeanFactory().getBean(requiredType);
}

3.再委托给DefaultListableBeanFactory#getBean(java.lang.Class)

getBeanFactory()实际上返回的是DefaultListableBeanFactory实例。到这里可以得知,DefaultListableBeanFactory是Spring当中的BeanFactory的默认实现。因此默认情况下,我们所说的IOC容器就是它了。本篇文章暂不会对容器做过多解析,先借助FactoryBean来初探其容貌。

4.再委托给DefaultListableBeanFactory#getBean(java.lang.Class, java.lang.Object…)

以上方法实际上还是没有做什么事情,依然是委托给getBean的一个重载方法。源码如下:

public <T> T getBean(Class<T> requiredType, @Nullable Object... args) throws BeansException {
  Assert.notNull(requiredType, "Required type must not be null");
  Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
  if (resolved == null) {
  throw new NoSuchBeanDefinitionException(requiredType);
  }
  return (T) resolved;
  }

5.再委托给DefaultListableBeanFactory#resolveBean

最终我们终于见到了庐山真面目,通过委托给内部的resolveBean()方法,改方法就是最终解析bean的方法。其源码如下:

private <T> T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
	// 解析bean,如果返回结果不为空,则说明已经获取到解析的bean了,直接返回即可。该方法是我们重点解析的方法,没有之一!
  NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
  if (namedBean != null) {
  	return namedBean.getBeanInstance();
  }
  // 如果返回的为空,则通过beanFactory来创建bean
  BeanFactory parent = getParentBeanFactory();
  if (parent instanceof DefaultListableBeanFactory) {
  	return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
  }
  else if (parent != null) {
  	ObjectProvider<T> parentProvider = parent.getBeanProvider(requiredType);
  	if (args != null) {
  		return parentProvider.getObject(args);
  	}
  	else {
  		return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
  	}
  }
  return null;
}

我们重点关注的是getBeanNamesForType方法和getBean方法。

6.DefaultListableBeanFactory#getBeanNamesForType解析

public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
  	Class resolved = type.resolve();
  	// 这里获取到的resolved为class
  	// MyDemoFactoryBean中并没有定义其他属性,因此这里将进入if分支。
  	if (resolved != null && !type.hasGenerics()) {
  		// 这是getBeanNamesForType的一个重载方法,最终依然是调用doGetBeanNamesForType方法,请看下面的解析
  		return getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit);
  	}
  	else {
  		return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
  	}
  }
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		List<String> result = new ArrayList<>();

		// 检查所有的beanDefinitionNames.beanDefinition在执行 new AnnotationConfigApplicationContext(Config.class)时就以及解析完毕了。因此这里可以直接遍历循环。
		for (String beanName : this.beanDefinitionNames) {
			// 判断是否有别名。一般情况下,我们很少有使用别名的情况。在本例中也一样无别名,因此进入if分支
			if (!isAlias(beanName)) {
				try {
					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
					// Only check bean definition if it is complete.
					if (!mbd.isAbstract() && (allowEagerInit ||
							(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
									!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
                        // !!! 重要!这里将根据beanName和beanDefinition来判断当前type是否FactoryBean类型。这个参数是区分普通bean和factoryBean的核心。
                        // 请看下面的第7小节的isFactoryBean()方法解析
						boolean isFactoryBean = isFactoryBean(beanName, mbd);
						BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
                        // !!! 重要!是否类型匹配,默认为false.
						boolean matchFound = false;
                        // 是否允许FactoryBean初始化,默认情况下是允许的
						boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName);
						boolean isNonLazyDecorated = dbd != null && !mbd.isLazyInit();
						// 接下来,主要是设置matchFound参数。
                        // 如果不是factoryBean类型,则直接调用isTypeMatch方法来设置matchFound参数,普通bean都是走的这个逻辑。
						if (!isFactoryBean) {
							if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
						}
                        // 如果是factoryBean,进入下面这个else分支
						else  {
						// 首先根据普通的beanName来尝试设置matchFound,如果为true,说明找的是FactoryBean中产生的beanName。
							if (includeNonSingletons || isNonLazyDecorated ||
									(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
                            // !!!重要! 如果matchFound依然为false,则说明很有可能是找FactoryBean本身,那么对beanName添加前缀:&,来尝试寻找FactoryBean本身。
							if (!matchFound) {
								// In case of FactoryBean, try to match FactoryBean instance itself next.
								beanName = FACTORY_BEAN_PREFIX + beanName;
								matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
							}
						}
                        // 如果matchFound为true,则将beanName添加到result中。注意,如果我们找的是FactoryBean类型的话,则beanName已经被添加&前缀了。
						if (matchFound) {
							result.add(beanName);
						}
					}
				}
				catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {
					if (allowEagerInit) {
						throw ex;
					}
					// Probably a placeholder: let's ignore it for type matching purposes.
					LogMessage message = (ex instanceof CannotLoadBeanClassException) ?
							LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) :
							LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName);
					logger.trace(message, ex);
					onSuppressedException(ex);
				}
			}
		}
        // ...省略其他代码...
        // 返回找到的beanName结果
		return StringUtils.toStringArray(result);
	}

7.AbstractBeanFactory#isFactoryBean解析

我们知道,如何判断是否是FactoryBean类型就是通过isFactoryBean()方法来解析的,其源码如下:

protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
		Boolean result = mbd.isFactoryBean;
		// 如果beanDefinition中的isFactoryBean属性为null,则直接获取bd中的beanType并判断是否是FactoryBean
        // 然后将判断结果赋值给bd的isFactoryBean属性,然后直接返回结果。
		if (result == null) {
			Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
			result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
			mbd.isFactoryBean = result;
		}
		return result;
}

8.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class, java.lang.Object…)

在ApplicationContext中getBea方法有很多重载,但其实最终都是解析出beanName,根据beanName来找到具体的bean。原因在于:在ApplicationContext中,beanDefinition、beanType、IOC容器等都是基于beanName来做映射关系的。因此我们在阅读源码时,可以发现很多时候都是首先解析出beanName再做进一步操作。又因为beanName的定义方式多种多样,因此解析过程也比较复杂,我们在解析源码时一定要静下心来。由于getBean方法本身涉及到bean的生命周期、循环依赖等复杂场景,所以将getBean方法单独拿出来进行解析。本篇文章我们只需要知道getBean方法能够获取到实际的bean即可。

总结

通过以上解析,我们大致知道了FactoryBean的运作原理。从源码中也可以得出几个结论:

FactoryBean本身就是一个比较特殊的bean; FactoryBean可以通过它的接口定义来生产一个普通bean; FactoryBean的beanName就是它生产的普通bean的beanName加一个“&”前缀,这也间接说明普通bean不可以使用“&”作为beanName的前缀,否则在启动时,容器会报错。 FactoryBean在我们的日常开发当中,如果用的好的能有奇效。具体效果可参考Spring是如何集成Mybatis的,它其实就是通过SqlSessionFactoryBean作为入口的,而SqlSessionFactoryBean其实就是一个FactoryBean。利用它屏蔽了Mybatis底层的复杂构建。如果工作中也有遇到类似的场景的话,可以借鉴此思路。