Spring IOC(七)类型推断

Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

Spring 容器中可以根据 beanName 查找其类型,其推断方式和 bean 的创建方式密切相关,并且 Spring 中有一个原则是尽可能的不要通过创建 bean 来获取其类型,除了 FactoryBean 只有通过创建对象调用其 getObjectType 方法,但也只是部分的创建该 FactoryBean(所谓的部分创建是指只实例化对象,而不进行属性注入和初始化过程):

  1. 对于缓存中存在单例的 bean,则直接根据对象获取其类型。
  2. 对于 FactoryBean 创建的 bean,Spring 会部分实例化,调用 FactoryBean#getObjectType() 方法。
  3. 对于工厂方法,Spring 不会实例化这个对象,而是用该方法的返回值类型来进行推断。如果该方法定义了泛型且返回值的类型恰好和泛型有关,则要通过传递的参数来进一步推断,但在 @Spring 5.1.3 似乎有 BUG。
  4. 工厂方法也有可能返回一个 FactoryBean 类型,静态工厂和实例工厂方法处理方法有些不一样。都有两种方式推断类型:一是根据方法的返回值类型进行推断;二是实例化这个 FactoryBean 后调用 getObjectType 方法。对于实例工厂,如果这个工厂还没有实例话就只执行第一种推断方式。

一、Spring 类型推断测试

(1) 环境准备

public class User {
} // 如果 FactoryBean 没有指定 User 类型则要部分实例化对象
public class UserFactoryBean implements FactoryBean<User> {
@Override
public User getObject() throws Exception {
return new User();
} @Override
public Class<?> getObjectType() {
return User.class;
}
} public class UserFactory {
// 1. 实例工厂
public User getObject1() {
return new User();
} // 2. 静态工厂
public static User getObject2() {
return new User();
} // 3.1 带参的工厂方法
public User getObject3(String username, String password) {
return new User();
} // 3.2 带泛型的工厂方法,但返回值类型和泛型无关
public <T, K, V> User getObject4(T t, String username) {
return new User();
} // 3.3 带泛型的工厂方法,但返回值类型由参数决定
public <T, K, V> T getObject5(T t) {
return t;
} // 4.1 静态工厂方法返回类型为 FactoryBean
public static FactoryBean<User> getObject6() {
return new UserFactoryBean();
} // 4.2 实例工厂方法返回类型为 FactoryBean
public FactoryBean<User> getObject7() {
return new UserFactoryBean();
}
}

(2) xcml 配置

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--1. 直接定义 className-->
<bean id="bean1" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.User"/> <!--2. FactoryBean-->
<bean id="bean2" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.UserFactoryBean"/> <!--3. 实例工厂-->
<bean id="userFactory" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.UserFactory"/>
<bean id="bean3" factory-bean="userFactory" factory-method="getObject1"/> <!--4. 静态工厂-->
<bean id="bean4" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.UserFactory" factory-method="getObject2"/> <!--5. 带参的工厂方法,和构造器注入类似-->
<!--5.1 工厂方法没有定义泛型-->
<bean id="bean5.1" factory-bean="userFactory" factory-method="getObject3">
<constructor-arg index="0" value="0"/>
<constructor-arg index="1" value="1"/>
</bean> <!--5.2 工厂方法定义泛型,但返回值类型与泛型无关-->
<bean id="bean5.2" factory-bean="userFactory" factory-method="getObject4">
<constructor-arg index="0" value="0"/>
<constructor-arg index="1" value="1"/>
</bean> <!--5.3 工厂方法定义泛型,返回值类型与参数类型决定-->
<bean id="bean5.3" factory-bean="userFactory" factory-method="getObject5">
<constructor-arg index="0" value="0"/>
</bean> <!--6 静态工厂方法的返回值为一个 FactoryBean 类型-->
<bean id="bean6.1" class="com.github.binarylei.spring.beans.factory.test.BeanFactoryTypeInferenceTest.UserFactory" factory-method="getObject6"/> <!--7 实例工厂方法的返回值为一个 FactoryBean 类型-->
<bean id="bean6.2" factory-bean="userFactory" factory-method="getObject7"/>
</beans>

(3) 测试一把

@Test
public void test() {
DefaultListableBeanFactory lbf = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(lbf);
reader.loadBeanDefinitions(new ClassPathResource("spring-context-factory.xml")); // 1. 直接定义 className
Assert.assertTrue(lbf.isTypeMatch("bean1", User.class));
// 2. FactoryBean
Assert.assertTrue(lbf.isTypeMatch("bean2", User.class));
// 3. 实例工厂
Assert.assertTrue(lbf.isTypeMatch("bean3", User.class));
// 4. 静态工厂
Assert.assertTrue(lbf.isTypeMatch("bean4", User.class));
// 5.1 工厂方法没有定义泛型
Assert.assertTrue(lbf.isTypeMatch("bean5.1", User.class));
// 5.2 工厂方法定义泛型,但返回值类型与泛型无关
Assert.assertTrue(lbf.isTypeMatch("bean5.2", User.class));
// 5.3 工厂方法定义泛型,返回值类型与参数类型决定,无法获取 @Spring 5.1.3
Assert.assertFalse(lbf.isTypeMatch("bean5.3", User.class));
// 6.1 静态工厂方法返回类型为 FactoryBean
Assert.assertTrue(lbf.isTypeMatch("bean6.1", User.class)); // 6.2 实例工厂方法返回类型为 FactoryBean,如果实例化这个工厂后可以获取其类型
Assert.assertFalse(lbf.isTypeMatch("bean6.2", User.class));
lbf.getBean("userFactory");
Assert.assertTrue(lbf.isTypeMatch("bean6.2", User.class));
}

可以看到工厂方法带有泛型且返回值类型和泛型有关后 Spring 不能正确处理了,另外实例工厂的工厂方法返回 FactoryBean 也不能正确处理。

二、Spring 类型推断源码分析

2.1 类型匹配的入口 - isTypeMatch

@Override
public boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
return isTypeMatch(name, ResolvableType.forRawClass(typeToMatch));
} @Override
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
String beanName = transformedBeanName(name); // 1. 根据实例化后的对象获取 bean 的类型,注意 FactoryBean 的处理即可
Object beanInstance = getSingleton(beanName, false);
if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
// 1.1 如果是 FactoryBean 类型,此时需要判断是否要查找的就是这个工厂对象,判断 beanName 是否是以 & 开头
// 如果是其创建的类型,则需要调用 getTypeForFactoryBean 从这个 FactoryBean 实例中获取真实类型
if (beanInstance instanceof FactoryBean) {
if (!BeanFactoryUtils.isFactoryDereference(name)) {
Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
return (type != null && typeToMatch.isAssignableFrom(type));
} else {
return typeToMatch.isInstance(beanInstance);
}
// 1.2 如果是普通 bean 则可直接判断类型,当然 Spring 还考虑的泛型的情况
} else if (!BeanFactoryUtils.isFactoryDereference(name)) {
if (typeToMatch.isInstance(beanInstance)) {
return true;
} else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
// AOP 有关 ????
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
Class<?> targetType = mbd.getTargetType();
if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance) &&
typeToMatch.isAssignableFrom(targetType)) {
Class<?> classToMatch = typeToMatch.resolve();
return (classToMatch == null || classToMatch.isInstance(beanInstance));
}
}
}
return false;
} else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
// null instance registered
return false;
} // 2. 父工厂,没什么好说的
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
} // 3. 下面就要从 bean 的定义中获取该 bean 的类型了
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); Class<?> classToMatch = typeToMatch.resolve();
if (classToMatch == null) {
classToMatch = FactoryBean.class;
}
Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch}); // 3.1 AOP 代理时会将原始的 BeanDefinition 存放到 decoratedDefinition 属性中,可以行忽略这部分
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
return typeToMatch.isAssignableFrom(targetClass);
}
} // 3.2 predictBeanType 推断 beanName 的类型,主要的逻辑
Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
if (beanType == null) {
return false;
} // 3.3 处理 FactoryBean 类型
if (FactoryBean.class.isAssignableFrom(beanType)) {
if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {
// 此时需要从 FactoryBean 中推断出真实类型
beanType = getTypeForFactoryBean(beanName, mbd);
if (beanType == null) {
return false;
}
}
// 3.4 beanType 不是 FactoryBean 类型,但是又要获取 FactoryBean 的类型???
} else if (BeanFactoryUtils.isFactoryDereference(name)) {
// Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
// type but we nevertheless are being asked to dereference a FactoryBean...
// Let's check the original bean class and proceed with it if it is a FactoryBean.
beanType = predictBeanType(beanName, mbd, FactoryBean.class);
if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
return false;
}
} // 3.5 优先从缓存中判断,可以比较泛型
ResolvableType resolvableType = mbd.targetType;
if (resolvableType == null) {
resolvableType = mbd.factoryMethodReturnType;
}
if (resolvableType != null && resolvableType.resolve() == beanType) {
return typeToMatch.isAssignableFrom(resolvableType);
}
return typeToMatch.isAssignableFrom(beanType);
}

上面的代码一大堆,逻辑也比较复杂,我们现在只需要明白三点:

  1. Spring 先直接从缓存中获取这个 bean 实例,再根据对象推断其类型。如果 bean 还没有创建或者干脆就不是单例,则只能根据定义这个 bean 的 BeanDefinition 获取了。
  2. 先根据 BeanDefinition 获取其当前 bean 的类型,Spring 全部委托给了 predictBeanType(beanName, mbd, typesToMatch) 方法。
  3. 如果当前 bean 是 FactoryBean,则还需要进一步获取其真实类型。这个过程由 getTypeForFactoryBean 方法完成,这个方法有两个重载的方法,即可以直接通过实例对象获取,也可以通过定义的 BeanDefinition 获取对象类型。

下面我们就分别介绍一下这两个方法。

2.2 从 BeanDefinition 推断类型 - predictBeanType

我们先从简单的看起,AbstractBeanFactory#predictBeanType 实现了这个方法,其子类又重载了这个方法 AbstractAutowireCapableBeanFactory#predictBeanType。

(1) AbstractBeanFactory#predictBeanType

protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
// 1. 直接从缓存中获取
Class<?> targetType = mbd.getTargetType();
if (targetType != null) {
return targetType;
}
// 2. 工厂方法一律返回 null,子类会重载后解析对应的 getFactoryMethodName
if (mbd.getFactoryMethodName() != null) {
return null;
}
// 3. 解析 BeanDefinition 的 className,如果已经加载则直接从缓存中获取
return resolveBeanClass(mbd, beanName, typesToMatch);
}

可以看到最终是从 BeanDefinition 中解析配置的 className 属性,将其加载到 JVM 中。它有三个参数,毫无疑问的是 mbd 是必须的,beanName 记录异常时使用,那 typesToMatch 是干嘛的呢?带着这个疑问我们看一下源码。

protected Class<?> resolveBeanClass(final RootBeanDefinition mbd, String beanName,
final Class<?>... typesToMatch) throws CannotLoadBeanClassException {
if (mbd.hasBeanClass()) {
return mbd.getBeanClass();
}
return doResolveBeanClass(mbd, typesToMatch);
}
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch)
throws ClassNotFoundException { ClassLoader beanClassLoader = getBeanClassLoader();
ClassLoader classLoaderToUse = beanClassLoader;
// 1. Spring 自定义的类型器 DecoratingClassLoader 修改了 JDK 的类加载规则,自己先加载一把,没有再特派给父加载器
// 这就产生了一个问题,每个临时的类加载器可能加载同一个类可能出现多个
// 所以可以将其加入到 excludeClass 仍采用双亲委派
if (!ObjectUtils.isEmpty(typesToMatch)) {
ClassLoader tempClassLoader = getTempClassLoader();
if (tempClassLoader != null) {
classLoaderToUse = tempClassLoader;
if (tempClassLoader instanceof DecoratingClassLoader) {
DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
for (Class<?> typeToMatch : typesToMatch) {
dcl.excludeClass(typeToMatch.getName());
}
}
}
}
// 2. 如果 className 含有点位符,解析后发生了变化,则不用缓存,以后每次都解析一次
String className = mbd.getBeanClassName();
if (className != null) {
Object evaluated = evaluateBeanDefinitionString(className, mbd);
if (!className.equals(evaluated)) {
if (evaluated instanceof Class) {
return (Class<?>) evaluated;
} else if (evaluated instanceof String) {
return ClassUtils.forName((String) evaluated, classLoaderToUse);
} else {
throw new IllegalStateException("Invalid class name expression result: " + evaluated);
}
}
if (classLoaderToUse != beanClassLoader) {
return ClassUtils.forName(className, classLoaderToUse);
}
}
// 3. 解析 className 后缓存起来
return mbd.resolveBeanClass(beanClassLoader);
}

至此,根据 BeanDefinition 解析 bean 的类型就完成了,最终还是回到了我们在 xml 文件中配置的 class 上来了,只是 Spring 的解析考虑了很多情况,一下子就复杂起来了。 想必现在对 typesToMatch 也有一定的了解了,就是为了保证要匹配的类型是同一个类加载器加载的,这里也就是 JDK 的系统类加载器 - AppClassLoader,这样调用 typeToMatch.isAssignableFrom(type) 方法才有意义。

(2) AbstractAutowireCapableBeanFactory#predictBeanType

AbstractAutowireCapableBeanFactory 又对 predictBeanType 进行了重载,增加了对工厂方法 factoryMethodName 的解析。

@Override
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
// 1. 增加了对工厂 factoryMethod 的解析
Class<?> targetType = determineTargetType(beanName, mbd, typesToMatch); // 2. 后置处理器 SmartInstantiationAwareBeanPostProcessors
if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
Class<?> predicted = ibp.predictBeanType(targetType, beanName);
if (predicted != null && (typesToMatch.length != 1 || FactoryBean.class != typesToMatch[0] ||
FactoryBean.class.isAssignableFrom(predicted))) {
return predicted;
}
}
}
}
return targetType;
} // 怎么样,逻辑还是之前的一样,只是增加对 factoryMethodName 的处理
protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
Class<?> targetType = mbd.getTargetType();
if (targetType == null) {
targetType = (mbd.getFactoryMethodName() != null ?
getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
resolveBeanClass(mbd, beanName, typesToMatch));
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
mbd.resolvedTargetType = targetType;
}
}
return targetType;
}

在这里,我们重点关注 getTypeForFactoryMethod 方法,Spring 是如何从一个工厂方法中获取其类型的。

protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
// 1. 缓存中直接取
ResolvableType cachedReturnType = mbd.factoryMethodReturnType;
if (cachedReturnType != null) {
return cachedReturnType.resolve();
} Class<?> factoryClass;
boolean isStatic = true; // 2. 在 Spring 中有静态工厂和实例工厂之分,如果是实例工厂名称不能是当前的 beanName
String factoryBeanName = mbd.getFactoryBeanName();
if (factoryBeanName != null) {
if (factoryBeanName.equals(beanName)) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"factory-bean reference points back to the same bean definition");
}
// 2.1 获取实例工厂的类型
factoryClass = getType(factoryBeanName);
isStatic = false;
} else {
// 2.2 静态工厂只能是静态方法,解析自己的 BeanDefinition 就可以了
factoryClass = resolveBeanClass(mbd, beanName, typesToMatch);
} if (factoryClass == null) {
return null;
}
// 2.3 如果是 CGLIB 代理,获取其真实类型
factoryClass = ClassUtils.getUserClass(factoryClass); // 3.1 如果有多个方法同名,则取其返回傎的公有类型,除非为 null
Class<?> commonType = null;
// 3.2 缓存这个工厂方法
Method uniqueCandidate = null;
// 3.3 如果方法含有泛型,则需要根据传递的参数进一步判断返回值类型
int minNrOfArgs =
(mbd.hasConstructorArgumentValues() ? mbd.getConstructorArgumentValues().getArgumentCount() : 0);
Method[] candidates = this.factoryMethodCandidateCache.computeIfAbsent(
factoryClass, ReflectionUtils::getUniqueDeclaredMethods); // 4. 遍历所有的名称为工厂方法的
for (Method candidate : candidates) {
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
candidate.getParameterCount() >= minNrOfArgs) {
// 4.1 方法中定义了泛型,则需要将配置的构造参数进行比较了,但似乎有问题
// 4.1.1 getTypeParameters 获取定义的泛型个数
if (candidate.getTypeParameters().length > 0) {
// 4.1.2 getParameterTypes 获取该方法的参数列表
Class<?>[] paramTypes = candidate.getParameterTypes();
String[] paramNames = null;
ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
Object[] args = new Object[paramTypes.length];
// 4.1.3 根据类型和参数名匹配真实的参数
for (int i = 0; i < args.length; i++) {
ConstructorArgumentValues.ValueHolder valueHolder = cav.getArgumentValue(
i, paramTypes[i], (paramNames != null ? paramNames[i] : null), usedValueHolders);
if (valueHolder == null) {
valueHolder = cav.getGenericArgumentValue(null, null, usedValueHolders);
}
if (valueHolder != null) {
args[i] = valueHolder.getValue();
usedValueHolders.add(valueHolder);
}
}
// 4.1.4 解析返回值对应的泛型类型
Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
candidate, args, getBeanClassLoader());
uniqueCandidate = (commonType == null && returnType == candidate.getReturnType() ?
candidate : null);
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
if (commonType == null) {
return null;
}
// 4.2 直接获取方法的返回值类型,如果有多个同名方法取其公共的祖先即可,没有就直接返回 null
} else {
// 第一次匹配上会缓存这个方法,但再次匹配上了,不好意思有重名的方法,直接清空
uniqueCandidate = (commonType == null ? candidate : null);
commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
if (commonType == null) {
return null;
}
}
}
} // 5.1 如果同名的方法只有一个,那么将这个工厂方法直接缓存起来
mbd.factoryMethodToIntrospect = uniqueCandidate;
if (commonType == null) {
return null;
}
// 5.2 如果只有唯一的工厂方法,那么尽可能缓存完型的类型,包括泛型。所以再解析一次用于缓存
cachedReturnType = (uniqueCandidate != null ?
ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType));
mbd.factoryMethodReturnType = cachedReturnType;
return cachedReturnType.resolve();
}

根据工厂方法推断 bean 的类型看起来很复杂,但逻辑还是很清晰的:

  1. 直接从缓存中获取这个工厂方法的返回值类型。
  2. 解析工厂方法的类型,这里分为静态工厂和实例工厂,静态工厂即为自己本身,实例工厂为配置的 factory-bean
  3. 遍历这个工厂类的每个和 factory-method 同名的工厂方法,包括其父类。Spring 增加了对泛型返回值方法的支持,但我在测试的时候失败了,不知道是不是使用不对。总之,就是遍历这些方法的返回值类型,如果有多个就取其公共的类型。
  4. 如果只有唯一的一个工厂方法,缓存起来,同时缓存解析后的类型,尽可能保存完整的类型,包括泛型。

对于泛型返回值类型再多说一句,Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod( candidate, args, getBeanClassLoader()); 在测试时确实没有问题,后来发现是 Spring 在解析 xml 文件时对构造参数做了一个封装,也就是 valueHolder.getValue() 不是配置的类型,而是 TypedStringValue 类型,导致解析出现问题。

2.3 根据 FactoryBean 获取真实类型 - getTypeForFactoryBean

getTypeForFactoryBean 相对来说比较简单,我们先说这个方法。AbstractBeanFactory#getTypeForFactoryBean 实现了这个方法,其子类又重载了这个方法 AbstractAutowireCapableBeanFactory#getTypeForFactoryBean

(1) AbstractBeanFactory#getTypeForFactoryBean

protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
if (!mbd.isSingleton()) {
return null;
}
FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
return getTypeForFactoryBean(factoryBean);
} protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
return factoryBean.getObjectType();
}

怎么样是不是很简单,先创建这个 FactoryBean 的实例,然后调用其 getObjectType 方法获取真实类型。但问题又来了,我们仅仅需要获取这个 bean 的类型,却要实例化与之相关的一系列对象,是不是代价太昂贵了,怎么规避这个问题呢?在其子类 AbstractAutowireCapableBeanFactory 中对这个方法做了进一步的增强,一起看一下。

(2) AbstractAutowireCapableBeanFactory#getTypeForFactoryBean

@Override
protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
// 1. 处理 mbd.getInstanceSupplier(),另外一种创建 bean 的方式,@Sping 5.0 提供,我们先忽略...
// if (mbd.getInstanceSupplier() != null) ... // 2. 这个 FactoryBean 也可能是其他的工厂的工厂方法创建的
String factoryBeanName = mbd.getFactoryBeanName();
String factoryMethodName = mbd.getFactoryMethodName(); // 3. 实例工厂创建 FactoryBean<User>
if (factoryBeanName != null) {
// 3.1 如果实例工厂的 className 已经解析过了,就直接从其工厂方法的返回值类型进行推断
if (factoryMethodName != null) {
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
if (fbDef instanceof AbstractBeanDefinition) {
AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
if (afbDef.hasBeanClass()) {
Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
if (result != null) {
return result;
}
}
}
}
// 3.2 判断这个实例工厂有没有实例化,如果没有则直接 Game Over
// 因为下面我们要调用 FactoryBean#getObjectType(),不能就是要部分实例化这个 FactoryBean 对象
// 如果连创建这个 FactoryBean 的工厂都未实例化,那么更谈不上创建 FactoryBean 了
// Spring 不会为了获取一个 bean 的类型去循环创建 bean
if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {
return null;
}
} // 4. 现在我们要先部分实例化这个 FactoryBean,调用其 getObjectType() 来获取对象类型
FactoryBean<?> fb = (mbd.isSingleton() ?
getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));
if (fb != null) {
// 4.1 调用 FactoryBean#getObjectType() 获取类型
Class<?> result = getTypeForFactoryBean(fb);
if (result != null) {
return result;
// 4.2 如果部分实例化还是获取不到,没办法了只有将这个 FactoryBean 全部实例化出来了
} else {
return super.getTypeForFactoryBean(beanName, mbd);
}
} // 5. 静态工厂创建 FactoryBean,这种情况下 fb=null,我们可以解析该方法的返回值类型
if (factoryBeanName == null && mbd.hasBeanClass()) {
// 5.1 解析方法的返回值的泛型类型,FactoryBean<User>
if (factoryMethodName != null) {
return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
// 5.2 该类就是一个 FactoryBean,则直接解析其泛型就好 FactoryBean<User>
} else {
return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
}
}
return null;
}

这个方法思路很清晰,就是在尽可能不去实例化对象的情况下,拿到这个 FactoryBean 的真实创建的类型。重点关注下面三个方法:

  1. getTypeForFactoryBeanFromMethod 工厂方法创建 FactoryBean<...>,直接解析其中的泛型
  2. getSingletonFactoryBeanForTypeCheckgetNonSingletonFactoryBeanForTypeCheck 都先尝试部分实例化这个 FactoryBean,如果任然无法获取,则需要完整的实例化这个对象。
private Class<?> getTypeForFactoryBeanFromMethod(Class<?> beanClass, final String factoryMethodName) {
class Holder {
Class<?> value = null;
}
final Holder objectType = new Holder();
Class<?> fbClass = ClassUtils.getUserClass(beanClass); // 解析工厂方法的返回值 FactoryBean<User> 类型,如果有多个同名的方法则取公共的类型
ReflectionUtils.doWithMethods(fbClass, method -> {
if (method.getName().equals(factoryMethodName) &&
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
// 解析 FactoryBean<User> 中的泛型类型
Class<?> currentType = GenericTypeResolver.resolveReturnTypeArgument(method, FactoryBean.class);
if (currentType != null) {
objectType.value = ClassUtils.determineCommonAncestor(currentType, objectType.value);
}
}
});
return (objectType.value != null && Object.class != objectType.value ? objectType.value : null);
}

getTypeForFactoryBeanFromMethod 实际上是在解析方法返回值 FactoryBean 中定义的范型。如下场景所示。

public class UserFactory {
public static FactoryBean<User> getObject6() {
return new UserFactoryBean();
}
}

至于 getSingletonFactoryBeanForTypeCheck 和 getNonSingletonFactoryBeanForTypeCheck 两个方法也只是实例化了这个 bean。调用了 createBeanInstance 方法。

private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
BeanWrapper bw = createBeanInstance(beanName, mbd, null);
instance = bw.getWrappedInstance();
return fb;
}
}

参考:

1 . 《》:<>


每天用心记录一点点。内容也许不重要,但习惯很重要!

Spring IOC(七)类型推断的更多相关文章

  1. Spring IOC及AOP学习总结

    一.Spring IOC体系学习总结: Spring中有两个容器体系,一类是BeanFactory.还有一类是ApplicationContext.BeanFactory提供了基础的容器功能.Appl ...

  2. Spring IOC知识点一网打尽!

    前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 在刷Spring书籍的时候花了点时间去学习了单例模式和工厂模式,总 ...

  3. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  4. Spring IOC源代码具体解释之容器初始化

    Spring IOC源代码具体解释之容器初始化 上篇介绍了Spring IOC的大致体系类图,先来看一段简短的代码,使用IOC比較典型的代码 ClassPathResource res = new C ...

  5. Spring IOC源代码具体解释之整体结构

    Spring ICO具体解释之整体结构 IOC介绍 IOC, spring的核心.贯穿Spring始终.直观的来说.就是由spring来负责控制对象的生命周期和对象间的关系,将对象之间的关系抽象出来. ...

  6. Spring IOC源代码具体解释之容器依赖注入

    Spring IOC源代码具体解释之容器依赖注入 上一篇博客中介绍了IOC容器的初始化.通过源代码分析大致了解了IOC容器初始化的一些知识.先简单回想下上篇的内容 加载bean定义文件的过程.这个过程 ...

  7. 程序员笔记|Spring IoC、面向切面编程、事务管理等Spring基本概念详解

    一.Spring IoC 1.1 重要概念 1)控制反转(Inversion of control) 控制反转是一种通过描述(在java中通过xml或者注解)并通过第三方去产生或获取特定对象的方式. ...

  8. Spring IoC源码解析之getBean

    一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到 ...

  9. Spring IOC 常用的注解

    一.@Bean 1.配置类 @Configuration public class MainConfig { @Bean public Person person(){ return new Pers ...

随机推荐

  1. wget 报错 OpenSSL: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failur

    解决办法 换成 curl -O -L xxxxxxxx

  2. elastic search文档详解

    在elastic search中文档(document)类似于关系型数据库里的记录(record),类型(type)类似于表(table),索引(index)类似于库(database). 文档一定有 ...

  3. helm 更改为国内源

     helm init --upgrade -i slpcat/tiller:v2.8.2 --stable-repo-url https://kubernetes.oss-cn-hangzhou.al ...

  4. SQL Server中row_number的用法

    ROW_NUMBER()函数将针对SELECT语句返回的每一行,从1开始编号,赋予其连续的编号.在查询时应用了一个排序标准后,只有通过编号才能够保证其顺序是一致的,当使用ROW_NUMBER函数时,也 ...

  5. pep8 && pep20

    pep8(部分,遇到问题再补充) 1.不建议使用tab键来空格,避免不必要的空格.操作符左右各加一个空格,函数默认参数使用的赋值符左右省略空格. 2.类和top-level函数定义之间空两行:类中的方 ...

  6. jQuery禁止Ajax请求缓存

    一 现象 get请求在有些浏览器中会缓存.浏览器不会发送请求,而是使用上次请求获取到的结果. post请求不会缓存.每次都会发送请求. 二 解决 jQuery提供了禁止Ajax请求缓存的方法: $.a ...

  7. RocketMq顺序消费

    部分内容出处   https://www.jianshu.com/p/453c6e7ff81c rocketmq内部有4个默认的队里,在发送消息时,同一组的消息需要按照顺序,发送到相应的mq中,同一组 ...

  8. CentOS常用的文件操作命令总结

    我可以说是linux操作新手,有些命令经常忘记,特别是对文件的某些操作,经常要翻阅之前的笔记,今天把之前在百度上整理的“CentOS常用的文件操作命令”转载到我的新博客上面,以供后面查阅! 博客后面还 ...

  9. java调用微信扫一扫

    步骤: 1,获取Accesstoken(参考我之前的文章) 2,获取jsapiticket(参考我之前的文章) 3,获取签名 4JSSDK使用步骤 步骤一:绑定域名(JS接口安全域名),.否则会报in ...

  10. OC 开发规范指南 - 个人见解写的很好

    纽约时报 移动团队 Objective-C 规范指南 这份规范指南概括了纽约时报 iOS 团队的代码约定. 介绍 关于这个编程语言的所有规范,如果这里没有写到,那就在苹果的文档里: • Objecti ...