Серия исходных кодов Spring — Анализ исходного кода FactoryBean

Spring

предисловие

В реальной разработке, если вы предпочитаете разработку системной инфраструктуры, вам не следует быть особенно незнакомым с использованием FactoryBean. FactoryBean и BeanFactory часто сравнивают, потому что их названия кажутся сбивающими с толку, но принципы и функции этих двух совершенно разные. В этой статье основное внимание будет уделено анализу исходного кода FactoryBean.

Интерфейс FactoryBean

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

Реализовать FactoryBean

@Component("myDemoService")
public class MyDemoFactoryBean implements FactoryBean<MyDemoService> {
    @Override
    public MyDemoService getObject() throws Exception {
        return new MyDemoService();
    }

    @Override
    public Class<?> getObjectType() {
        return MyDemoService.class;
    }
}

Давайте напишем класс для тестирования и посмотрим, сможем ли мы получить два bean-компонента MyDemoFactoryBean и MyDemoService из контейнера:

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

Результат выполнения следующий:

["&myDemoService"]
["myDemoService"]

Видно, что два компонента, MyDemoFactoryBean и MyDemoService, действительно могут быть получены из контейнера IOC. это означает:

  • FacotryBean также является компонентом, управляемым контейнером IOC;
  • FacotryBean может создать указанный компонент, то есть объект, возвращаемый FacotryBean#getObject();
  • BeanName: по умолчанию BeanName FactoryBean имеет вид [&] + [имя класса ]; а beanName производимого им компонента — [имя класса ].

Далее давайте посмотрим, как FactoryBean разрешается в Spring.

Отслеживание источника FactoryBean

1. Вход

В Spring самый прямой способ отслеживать исходный код — через applicationContext.getBean.

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

2. Делегировать в AbstractApplicationContext#getBean

Фактический вызов — это AbstractApplicationContext#getBean(java.lang.Class), который на самом деле является методом пустой оболочки, фактически делегированным методу getBean BeanFactory.

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

3. Делегировать DefaultListableBeanFactory#getBean(java.lang.Class)

getBeanFactory() фактически возвращает экземпляр DefaultListableBeanFactory. Как вы можете видеть здесь, DefaultListableBeanFactory — это реализация BeanFactory по умолчанию в Spring. Так что по умолчанию то, что мы называем контейнером 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

В конце концов, мы, наконец, увидели истинное лицо Mount Lu. Делегируя внутренний метод resolveBean(), изменение метода является окончательным методом для разрешения компонента. Его исходный код выглядит следующим образом:

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

5. Анализ исходного кода DefaultListableBeanFactory#resolveNamedBean

private <T> NamedBeanHolder<T> resolveNamedBean(
			ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {

		Assert.notNull(requiredType, "Required type must not be null");
  		// 根据type获取beanName.该方法便隐藏着FactoryBean的真实身份。
		String[] candidateNames = getBeanNamesForType(requiredType);
		// 如果解析出的beanName不止1个,则自动解析到其关联的bean。
		if (candidateNames.length > 1) {
			List<String> autowireCandidates = new ArrayList<>(candidateNames.length);
			for (String beanName : candidateNames) {
				if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
					autowireCandidates.add(beanName);
				}
			}
			if (!autowireCandidates.isEmpty()) {
				candidateNames = StringUtils.toStringArray(autowireCandidates);
			}
		}
		// 如果beanName只有1个,则直接调用getBean方法。很显然,只有一个beanName。
		if (candidateNames.length == 1) {
			String beanName = candidateNames[0];
			return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
		}
		else if (candidateNames.length > 1) {
			Map<String, Object> candidates = new LinkedHashMap<>(candidateNames.length);
			for (String beanName : candidateNames) {
				if (containsSingleton(beanName) && args == null) {
					Object beanInstance = getBean(beanName);
					candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
				}
				else {
					candidates.put(beanName, getType(beanName));
				}
			}
			String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
			if (candidateName == null) {
				candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
			}
			if (candidateName != null) {
				Object beanInstance = candidates.get(candidateName);
				if (beanInstance == null || beanInstance instanceof Class) {
					beanInstance = getBean(candidateName, requiredType.toClass(), args);
				}
				return new NamedBeanHolder<>(candidateName, (T) beanInstance);
			}
			if (!nonUniqueAsNull) {
				throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
			}
		}

		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...)

Существует много перегрузок метода getBea в ApplicationContext, но на самом деле beanName окончательно разрешается, и конкретный bean-компонент находится в соответствии с beanName. Причина в том, что в ApplicationContext beanDefinition, beanType, контейнер IOC и т. д. отображаются на основе beanName. Поэтому, когда мы читаем исходный код, мы можем обнаружить, что во многих случаях сначала анализируется beanName, а затем выполняются дальнейшие операции. А так как beanName определяется по-разному, то и процесс парсинга усложняется, надо успокоиться при парсинге исходного кода. Поскольку сам метод getBean включает в себя сложные сценарии, такие как жизненный цикл bean-компонентов и циклические зависимости, метод getBean вынесен для анализа отдельно. В этой статье нам нужно только знать, что метод getBean может получить фактический компонент.

Суммировать

Благодаря приведенному выше анализу мы примерно знаем принцип работы FactoryBean. Из исходного кода также можно сделать несколько выводов:

  • FactoryBean сам по себе является специальным bean-компонентом;
  • FactoryBean может создавать обычные bean-компоненты через определение своего интерфейса;
  • beanName для FactoryBean — это beanName обычного bean-компонента, который он производит, плюс префикс «&», что также косвенно означает, что обычные bean-компоненты не могут использовать «&» в качестве префикса beanName, иначе контейнер сообщит об ошибке при запуске.

В нашей повседневной разработке FactoryBean может давать удивительные результаты, если его правильно использовать. Чтобы узнать о конкретном эффекте, обратитесь к тому, как Spring интегрирует Mybatis, Он фактически использует SqlSessionFactoryBean в качестве точки входа, а SqlSessionFactoryBean на самом деле является FactoryBean. Используйте его, чтобы защитить сложную конструкцию лежащего в основе Мибатиса. Если вы сталкивались с подобными сценариями в своей работе, вы можете извлечь уроки из этой идеи.